Yattm - unified GTK instant-messaging client logo
   [Generated for version 0.2-17 - Mon Jan 6 19:01:23 GMT+1 2003]

Home - Main Page - Data Structures - File List - Data Fields - Globals

status.c

Go to the documentation of this file.
00001 /*
00002  * Yattm 
00003  *
00004  * Copyright (C) 1999, Torrey Searle <tsearle@uci.edu>
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  */
00021 
00022 /*
00023  * status.c
00024  * code for making the status changing widgets
00025  *
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #  include <config.h>
00030 #endif
00031 
00032 #include "intl.h"
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <assert.h>
00036 
00037 #ifndef __MINGW32__
00038 #include <X11/Xlib.h>
00039 #endif
00040 
00041 #include "globals.h"
00042 #include "status.h"
00043 #include "chat_window.h"
00044 #include "about.h"
00045 #include "help_menu.h"
00046 #include "util.h"
00047 #include "add_contact_window.h"
00048 #include "dialog.h"
00049 #include "away_window.h"
00050 #include "message_parse.h"
00051 #include "contact_actions.h"
00052 #include "sound.h"
00053 #include "plugin.h"
00054 #include "prefs.h"
00055 
00056 #include "pixmaps/login_icon.xpm"
00057 #include "pixmaps/blank_icon.xpm"
00058 #include "pixmaps/logoff_icon.xpm"
00059 
00060 
00061 enum {
00062     TARGET_STRING,
00063     TARGET_ROOTWIN,
00064     TARGET_URL
00065 };
00066 
00067 struct acctStatus
00068 {
00069     eb_local_account * ela;
00070     gint status;
00071 };
00072 
00073 
00074 /* globals - referenced in gtk_globals.h */
00075 GtkWidget *statuswindow = NULL;
00076 GtkWidget *away_menu;
00077 
00078 
00079 static GtkTooltips * status_tips=NULL;
00080 
00081 static GtkWidget * contact_list;
00082 static GtkWidget * contact_window;
00083 static GtkWidget * status_bar = 0;
00084 static GtkWidget * status_message;
00085 
00086 /* status_show is:
00087    0 => show all accounts,
00088    1 => show all contacts,
00089    2 => show online contacts
00090 */
00091 static int status_show = 2;
00092 
00093 static time_t last_sound_played = 0;
00094 
00095 
00096 void focus_statuswindow (void) 
00097 {
00098     gdk_window_raise(statuswindow->window);
00099 }
00100 
00101 static void delete_event( GtkWidget *widget,
00102                    GdkEvent *event,
00103                    gpointer data )
00104 {
00105     GList * node = accounts;
00106     gchar *userrc = NULL;
00107     while(node)
00108     {
00109         if(node->data && ((eb_local_account*)(node->data))->connected)
00110         RUN_SERVICE(((eb_local_account*)(node->data)))->logout(node->data);
00111         node = node->next;
00112     }
00113     
00114     userrc = g_strconcat(config_dir, G_DIR_SEPARATOR_S, "menurc", NULL);
00115     gtk_item_factory_dump_rc(userrc, NULL, TRUE);
00116     g_free(userrc);
00117 
00118     gtk_main_quit();
00119 }
00120 
00121 
00122 static void send_file_callback(GtkWidget * w, eb_account * ea )
00123 {
00124     eb_do_send_file(ea);
00125 }
00126 
00127 static void send_file_with_contact_callback(GtkWidget * w, struct contact * conn )
00128 {
00129     eb_account * ea = find_suitable_remote_account( NULL, conn );
00130 
00131     eb_do_send_file(ea);
00132 }
00133 
00134 /*** MIZHI
00135  * For displaying the logs
00136  */
00137 static void view_log_callback(GtkWidget * w, struct contact * conn)
00138 {  
00139     eb_view_log(conn);
00140 }
00141 
00142 static void edit_trigger_callback(GtkWidget * w, struct contact * conn)
00143 {
00144   show_trigger_window(conn);
00145 }
00146 
00147 
00148 static void edit_group_callback(GtkWidget * w, grouplist * g)
00149 {
00150     edit_group_window_new(g);
00151 }
00152 
00153 static void add_to_group_callback(GtkWidget *widget, grouplist *g)
00154 {
00155     show_add_contact_to_group_window(g);
00156 }
00157 
00158 static void edit_contact_callback(GtkWidget * w, struct contact * conn)
00159 {
00160   edit_contact_window_new(conn);
00161 }
00162 
00163 static void add_account_to_contact_callback(GtkWidget *w, struct contact *conn)
00164 {
00165   show_add_account_to_contact_window(conn);
00166 }
00167 
00168 static void edit_account_callback(GtkWidget * w, gpointer a)
00169 {
00170   edit_account_window_new(a);
00171 }
00172 
00173 
00174 static void remove_group_callback(GtkWidget *w, gpointer d)
00175 {
00176   if (gtk_object_get_user_data (GTK_OBJECT(w)) != 0)
00177     {
00178       grouplist * g = (grouplist *) d;
00179 
00180       eb_debug(DBG_CORE, "Delete Group");
00181       remove_group(g);
00182       
00183       update_contact_list ();
00184       write_contact_list();
00185     }
00186 }
00187 
00188 static void offer_remove_group_callback(GtkWidget *w, gpointer d)
00189 {
00190   if (d != 0)
00191     {
00192       grouplist * g = (grouplist *) d;
00193 
00194       char buff [1024];
00195       sprintf (buff, _("Do you really want to delete group \"%s\"?"), g->name);
00196 
00197       do_dialog (buff, _("Delete Group"), remove_group_callback, d);
00198     }
00199 }
00200 
00201 static void remove_contact_callback(GtkWidget *w, gpointer d)
00202 {
00203   if (gtk_object_get_user_data (GTK_OBJECT(w)) != 0)
00204     {
00205       struct contact * conn = (struct contact *) d;
00206 
00207       eb_debug(DBG_CORE, "Delete Account");
00208       remove_contact (conn);
00209       
00210       update_contact_list ();
00211       write_contact_list();
00212     }
00213 }
00214 
00215 static void offer_remove_contact_callback(GtkWidget *w, gpointer d)
00216 {
00217   if (d != 0)
00218     {
00219       struct contact * c = (struct contact *) d;
00220 
00221       char buff [1024];
00222       sprintf (buff, _("Do you really want to delete contact \"%s\"?"), c->nick);
00223 
00224       do_dialog (buff, _("Delete Contact"), remove_contact_callback, d);
00225     }
00226 }
00227 
00228 static void remove_account_callback(GtkWidget *w, gpointer d)
00229 {
00230   if (gtk_object_get_user_data (GTK_OBJECT(w)) != 0)
00231     {
00232       eb_debug(DBG_CORE, "Delete Account");
00233       remove_account(d);
00234       
00235       update_contact_list ();
00236       write_contact_list();
00237     }
00238 }
00239 
00240 static void offer_remove_account_callback(GtkWidget *w, gpointer d)
00241 {
00242   if (d != 0)
00243     {
00244       struct account * a = (struct account *) d;
00245 
00246       char buff [1024];
00247       sprintf (buff, _("Do you really want to delete account \"%s\"?"),
00248            a->handle);
00249 
00250       do_dialog (buff, _("Delete Account"), remove_account_callback, d);
00251     }
00252 }
00253 
00254 static void eb_save_size( GtkWidget * widget, gpointer data ) {
00255     int h = widget->allocation.height;
00256     int w = widget->allocation.width;
00257     if (h<10 || w < 10) return;
00258     iSetLocalPref("length_contact_window",h);
00259     iSetLocalPref("width_contact_window",w);
00260     write_prefs();
00261 }
00262 
00263 static void get_info(GtkWidget * w, eb_account *ea )
00264 {
00265    
00266   eb_local_account * el;
00267   //eb_account * ea = find_suitable_remote_account( NULL, conn );
00268 
00269   if(ea) {
00270       el = find_suitable_local_account(NULL, ea->service_id);
00271       RUN_SERVICE(ea)->get_info(el ,ea);  
00272   }  else {
00273       eb_debug(DBG_CORE, "Couldn't find suitable local account for info request");
00274       return;
00275   }  
00276 
00277 }
00278 
00279 static void update_status_message(gchar * message )
00280 {
00281     if(status_bar)
00282     {
00283         gtk_widget_destroy(status_message);
00284         status_message = gtk_label_new(message);
00285         gtk_container_add(GTK_CONTAINER(status_bar), status_message);
00286         gtk_widget_show(status_message);
00287     }
00288 }
00289 
00290 
00291 static void collapse_contact( GtkTreeItem * treeItem, gpointer data )
00292 {
00293     struct contact * c = data;
00294     c->expanded = FALSE;
00295 }
00296 
00297 static void expand_contact( GtkTreeItem * treeItem, gpointer data )
00298 {
00299     struct contact * c = data;
00300     c->expanded = TRUE;
00301 }
00302 
00303 static void status_show_callback(GtkWidget *w, gpointer data)
00304 {
00305     status_show = (int)data;
00306     update_contact_list ();
00307 
00308 }
00309 
00310 static GtkWidget *make_info_menu(struct contact *c)
00311 {
00312   GList *iterator;
00313   GtkWidget *InfoMenu = gtk_menu_new();
00314   GtkWidget *button;
00315   char *buff = NULL;
00316   for(iterator=c->accounts; iterator; iterator=iterator->next)
00317   {
00318     eb_account * account = (eb_account*)iterator->data;
00319     if(account->online){
00320       buff = g_strdup_printf("%s [%s]", account->handle, get_service_name(account->service_id));        
00321       button = gtk_menu_item_new_with_label(buff);
00322       free(buff);
00323       gtk_signal_connect(GTK_OBJECT(button), "activate", GTK_SIGNAL_FUNC(get_info),account);
00324       gtk_menu_append(GTK_MENU(InfoMenu), button);
00325       gtk_widget_show(button);
00326     }
00327   }
00328   return InfoMenu;
00329 }
00330 
00331 /*
00332  * Menus raised by buttons
00333  */
00334     
00335 static void group_menu(GdkEventButton * event, gpointer d )
00336 {
00337   GtkWidget *menu;
00338   
00339   menu = gtk_menu_new();
00340   
00341   eb_menu_button (GTK_MENU(menu), _("Add contact to group"),
00342           GTK_SIGNAL_FUNC(add_to_group_callback), d);
00343 
00344   eb_menu_button (GTK_MENU(menu), NULL, NULL, NULL);
00345 
00346   eb_menu_button (GTK_MENU(menu), _("Edit Group"),
00347           GTK_SIGNAL_FUNC(edit_group_callback), d);
00348 
00349   eb_menu_button (GTK_MENU(menu), _("Delete Group"),
00350           GTK_SIGNAL_FUNC(offer_remove_group_callback), d);
00351   
00352   gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
00353          event->button, event->time );
00354 }
00355 
00356 static void contact_menu(GdkEventButton * event, gpointer d )
00357 {
00358   struct contact *conn=d;
00359   GtkWidget *menu, *submenu, *button;
00360   menu_data *md=NULL;
00361   menu_item_data *mid=NULL;
00362   ebmContactData *ecd=NULL;
00363   GList *list=NULL;
00364   
00365   menu = gtk_menu_new();
00366   
00367   eb_menu_button (GTK_MENU(menu), _("Add Account to Contact"),
00368           GTK_SIGNAL_FUNC(add_account_to_contact_callback), d);
00369 
00370   eb_menu_button (GTK_MENU(menu), _("Edit Contact"),
00371           GTK_SIGNAL_FUNC(edit_contact_callback), d);
00372 
00373   eb_menu_button (GTK_MENU(menu), _("Delete Contact"),
00374           GTK_SIGNAL_FUNC(offer_remove_contact_callback), d);
00375   
00376   eb_menu_button (GTK_MENU(menu), _("Send File"),
00377           GTK_SIGNAL_FUNC(send_file_with_contact_callback), d);
00378   
00379   submenu = make_info_menu((struct contact *)d);
00380   eb_menu_submenu (GTK_MENU(menu), _("Info"), submenu);
00381 
00382   eb_menu_button (GTK_MENU(menu), _("Edit Trigger"),
00383           GTK_SIGNAL_FUNC(edit_trigger_callback), d);
00384 
00385   /*** MIZHI
00386    * code for viewing the logs
00387    */
00388   eb_menu_button (GTK_MENU(menu), _("View Log"),
00389           GTK_SIGNAL_FUNC(view_log_callback), d);
00390 
00391   md = GetPref(EB_CONTACT_MENU);
00392   if(md)
00393     {
00394       for(list = md->menu_items; list; list  = g_list_next(list) )
00395         {
00396           ecd=ebmContactData_new();
00397           ecd->contact=conn->nick;
00398           mid=(menu_item_data *)list->data;
00399           mid->data=(ebmCallbackData *)ecd;
00400           eb_debug(DBG_CORE, "adding chat window item: %s\n", mid->label);
00401           button = gtk_menu_item_new_with_label(mid->label);
00402           gtk_menu_append(GTK_MENU(menu), button);
00403           gtk_signal_connect(GTK_OBJECT(button), "activate",
00404           eb_generic_menu_function, mid);
00405           gtk_widget_show(button);  
00406         }
00407     }
00408 
00409   gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
00410          event->button, event->time );
00411 }
00412 
00413 
00414 static void account_menu(GdkEventButton * event, gpointer d )
00415 {
00416   GtkWidget * menu;
00417 
00418   menu = gtk_menu_new();
00419 
00420   eb_menu_button (GTK_MENU(menu), _("Edit Account"),
00421           GTK_SIGNAL_FUNC(edit_account_callback), d);
00422 
00423   eb_menu_button (GTK_MENU(menu), _("Delete Account"),
00424           GTK_SIGNAL_FUNC(offer_remove_account_callback), d);
00425 
00426 
00427   eb_menu_button (GTK_MENU(menu), _("Send File"),
00428           GTK_SIGNAL_FUNC(send_file_callback), d);
00429 
00430   eb_menu_button (GTK_MENU(menu), _("Info"),
00431           GTK_SIGNAL_FUNC(get_info),d);
00432 
00433   gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
00434          event->button, event->time );
00435 
00436 }
00437 
00438 /*
00439  * End accounts raised by buttons
00440  */
00441 
00442 
00443 /*
00444  * Mouse button event handlers for elements of the group/contact/account list
00445  */
00446 
00447 static void group_click (GtkWidget *widget, GdkEventButton * event,
00448                          gpointer d)
00449 {
00450   if (event->type == GDK_BUTTON_PRESS && event->button == 3)
00451     {
00452       gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),
00453                    "button_press_event");
00454       group_menu (event, d);
00455     }
00456 }
00457 
00458 static void contact_click (GtkWidget *widget, GdkEventButton * event,
00459                          gpointer d)
00460 {
00461     if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
00462     {
00463             eb_chat_window_display_contact((struct contact *)d);
00464     }
00465     else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
00466     {
00467       gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),
00468                        "button_press_event");
00469       contact_menu (event, d);
00470     }
00471 }
00472 
00473 
00474 static void account_click (GtkWidget *widget, GdkEventButton * event,
00475                          gpointer d)
00476 {
00477   if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
00478     {
00479       eb_chat_window_display_account((eb_account *)d);
00480     }
00481   else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
00482     {
00483       gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),
00484                    "button_press_event");
00485       account_menu (event, d);
00486     }
00487 }
00488 
00489 /*
00490  * End Mouse button event handlers for elements of the group/contact/account
00491  * list
00492  */
00493 
00494 static void add_callback(GtkWidget *widget, GtkTree *tree)
00495 {
00496     show_add_contact_window();
00497 }
00498 
00499 static void add_group_callback(GtkWidget *widget, GtkTree *tree)
00500 {
00501     show_add_group_window();
00502 }
00503 
00504 static void eb_edit_accounts( GtkWidget * widget, gpointer stats )
00505 {
00506     eb_new_user();
00507 }
00508 
00509 static void build_prefs_callback( GtkWidget * widget, gpointer stats )
00510 {
00511     build_prefs();
00512 }
00513 
00514 static void launch_group_chat( GtkWidget * widget, gpointer userdata )
00515 {
00516     open_join_chat_window();
00517 }
00518 
00519 /* Be sure to free the menu items in the status menu */
00520 static void eb_status_remove(GtkContainer *container,  GtkWidget * widget, gpointer stats )
00521 {
00522     g_free(stats);
00523 }
00524 
00525 static void eb_status( GtkCheckMenuItem * widget, gpointer stats )
00526 {
00527     struct acctStatus *s;
00528     int current_state=0, new_state=0;;
00529 
00530     s = (struct acctStatus *)stats;
00531     eb_debug(DBG_CORE, "Status of radio button for state[%i]: %i\n", s->status, widget->active);
00532     current_state = eb_services[s->ela->service_id].sc->get_current_state(s->ela);
00533     /* We were called for the deactivating state, ignore it */
00534     if(!widget->active) {
00535             eb_debug(DBG_CORE, "Current state is %i\n", current_state);
00536         return;
00537     }
00538     eb_debug(DBG_CORE, "Setting %s to state %d\n", eb_services[s->ela->service_id].name, s->status);
00539     eb_debug(DBG_CORE, "Current state is %i\n", current_state);
00540     if ( current_state != s->status) {
00541         eb_debug(DBG_CORE, "Calling set_current_state: %i\n", s->status);
00542         eb_services[s->ela->service_id].sc->set_current_state(s->ela, s->status);
00543         new_state = eb_services[s->ela->service_id].sc->get_current_state(s->ela);
00544     /* Did the state change work? */
00545     if(new_state != s->status)
00546         /* No, set the radio button to the correct value */
00547         eb_set_active_menu_status(s->ela->status_menu, new_state);
00548     }
00549     eb_debug(DBG_CORE, "%s set to state %d.\n", eb_services[s->ela->service_id].name, s->status );
00550 
00551 }
00552 
00553 void eb_sign_on_all(GtkWidget *widget, gpointer foo) {
00554     GList *node = accounts ;
00555     while(node) {
00556         eb_local_account *ac = (eb_local_account*)(node->data);
00557         if (!ac->connected) {
00558             RUN_SERVICE(ac)->login(ac) ;
00559         }
00560         node = node->next ;
00561     }
00562 }
00563 
00564 static void eb_sign_off_all(GtkWidget *widget, gpointer foo) {
00565 
00566     GList *node = accounts ;
00567     while(node) {
00568         eb_local_account *ac = (eb_local_account*)(node->data);
00569         if (ac->connected) {
00570             RUN_SERVICE(ac)->logout(ac) ;
00571         }
00572         node = node->next ;
00573     }
00574 
00575 }
00576 
00577 static gint get_contact_position( struct contact * ec)
00578 {
00579     gint i=0;
00580     GList *l;
00581     
00582     for (l = ec->group->members; l && (l->data != ec); l=l->next) 
00583     {
00584         struct contact * contact = l->data;
00585         if (contact->list_item)
00586             i++;
00587     }
00588     return i;
00589 }
00590 
00591 static gint get_account_position( eb_account * ea)
00592 {
00593     gint i=0;
00594     GList *l;
00595     
00596     for (l = ea->account_contact->accounts; l && (l->data != ea); l=l->next) {
00597         eb_account * account = l->data;
00598         if (account->list_item)
00599             i++;
00600     }
00601     return i;
00602 }
00603 
00604 /* General purpose update Contact List */
00605 void update_contact_list ()
00606 {
00607   GList * grps;
00608   GList * contacts;
00609   GList * accounts;
00610 
00611   grouplist * grp;
00612   struct contact * con;
00613   eb_account * ea;
00614 
00615   /* Error Check */
00616   if ( (status_show < 0) || (status_show > 2) ) { status_show = 2; }
00617 
00618   for (grps = groups; grps; grps = grps->next)
00619     {
00620       grp = grps->data;
00621 
00622       /* Currently, groups are always visible so we never touch them :) */
00623 
00624       for (contacts = grp->members; contacts; contacts = contacts->next)
00625     {
00626       con = contacts->data;
00627 
00628       /* Visibility of contact */
00629 
00630       /* show_all_accounts presumes show_all_contacts */
00631       if ((status_show == 1) || (status_show == 0))
00632         {
00633           /* MUST show the contact */
00634           add_contact_line (con);
00635           contact_update_status (con);
00636         }
00637       else
00638         {
00639           /* defer to whether it's online or not */
00640 
00641           if (! con->online)
00642         {
00643           remove_contact_line (con);
00644         }
00645           else
00646         {
00647           contact_update_status (con);
00648         }
00649         }
00650 
00651       for (accounts = con->accounts; accounts; accounts = accounts->next)
00652         {
00653           ea = accounts->data;
00654 
00655           if ( (status_show == 0) || (status_show == 1) )
00656         {
00657           /* definitely visible */
00658 
00659           add_account_line(ea);
00660           buddy_update_status(ea);
00661 
00662           if (con->list_item == NULL)
00663             {
00664               /* Do nothing */
00665               fprintf (stderr,
00666             _("Account vanished after add_account_line.\n"));
00667             }
00668           else if (status_show == 0)
00669             {
00670               /* MAKE IT VISIBLE NOW */
00671 
00672             gtk_tree_item_expand (GTK_TREE_ITEM(con->list_item));
00673             }
00674           else
00675             {
00676             gtk_tree_item_collapse (GTK_TREE_ITEM(con->list_item));
00677             }
00678         }
00679           else
00680         {
00681           /* Close it up */
00682 
00683           if (ea->online)
00684             {
00685               buddy_update_status(ea);
00686 
00687               if (con->list_item != NULL)
00688                         {
00689                           gtk_tree_item_collapse
00690                 (GTK_TREE_ITEM(con->list_item));
00691                         }
00692                       else
00693                         {
00694                           fprintf (stderr, _("Account missing while online?\n"));
00695                         }
00696             }
00697           else
00698             {
00699               remove_account_line(ea);
00700             }
00701         }
00702         } /* End for loop for accounts */
00703 
00704     } /* End for loop for contacts */
00705 
00706     } /* End for loop for groups */
00707 
00708 } /* end update_contact_list */
00709 
00710 /* makes all accounts visible on the contact list */
00711 
00712 /* shows a contact tree and its accounts and explicitly draws
00713    each account rather than waiting for a login message from
00714    the server.  Useful for reshowing a contact after removing
00715    it from the buddy list. */
00716 void add_contact_and_accounts(struct contact * c)
00717 {
00718     GList *l;
00719     for (l = c->accounts; l; l = l->next) 
00720     {
00721         eb_account * ea = l->data;
00722         if ((status_show == 0) || (status_show == 1) || ea->online) 
00723         {
00724             add_account_line(ea);
00725             buddy_update_status(ea);
00726         }
00727     }
00728 }
00729 
00730 static GdkPixmap * iconlogin_pm = NULL;
00731 static GdkBitmap * iconlogin_bm = NULL;
00732 static GdkPixmap * iconblank_pm = NULL;
00733 static GdkBitmap * iconblank_bm = NULL;
00734 static GdkPixmap * iconlogoff_pm = NULL;
00735 static GdkBitmap * iconlogoff_bm = NULL;
00736 
00737 GtkTargetEntry drag_types[1] =
00738 {
00739     {"text/plain", GTK_TARGET_SAME_APP, 0}
00740 };
00741 
00742 static gpointer dndtarget = NULL;
00743 static gboolean drag_motion_cb(GtkWidget      *widget,
00744                       GdkDragContext *context,
00745                       gint            x,
00746                       gint            y,
00747                       guint           time,
00748                       gpointer        data)
00749 {
00750     dndtarget = data;
00751     return 1;
00752 }
00753 
00754 static void start_drag(GtkWidget *widget, GdkDragContext *dc, gpointer data)
00755 {
00756     dndtarget=NULL;
00757 }
00758 
00759 static void drag_data_get(GtkWidget        *widget,
00760                      GdkDragContext   *drag_context,
00761                      GtkSelectionData *selection_data,
00762                      guint             info,
00763                      guint             time,
00764                      gpointer          data)
00765 {
00766     grouplist *gl=(grouplist *)dndtarget;
00767     struct contact *ec = data;
00768     if(gl == NULL || ec == NULL) {
00769         gtk_drag_finish(drag_context, FALSE, FALSE, time);
00770         return;
00771     }
00772     move_contact(gl->name, ec);
00773     update_contact_list ();
00774     write_contact_list();
00775     gtk_drag_finish(drag_context, TRUE, TRUE, time);
00776 }
00777 
00778 /*
00779  * Add/Remove entries to/from box -- group, contact, account
00780  */
00781 
00782 /* makes a group visible on the contact list */
00783 void add_group_line(grouplist * eg)
00784 {
00785     GtkWidget * box;
00786 
00787     /* Might call add_group() - which calls add_group_line() 
00788        before contact_list exists - this is OK so just return */
00789     if (eg->list_item || !contact_list)
00790         return;
00791         
00792     eg->list_item = gtk_tree_item_new();
00793 
00794     box = gtk_hbox_new(FALSE, 1);
00795 
00796     eg->label = gtk_label_new(eg->name);
00797     gtk_label_set_justify(GTK_LABEL(eg->label), GTK_JUSTIFY_LEFT);
00798 
00799     gtk_box_pack_start(GTK_BOX(box), eg->label, FALSE, FALSE, 1);
00800     gtk_widget_show(eg->label);
00801 
00802     gtk_container_add(GTK_CONTAINER(eg->list_item), box);
00803     gtk_widget_show(box);
00804 
00805     gtk_object_set_user_data(GTK_OBJECT(eg->list_item), (gpointer)eg);
00806     eg->contacts_online = 0;
00807     eg->contacts_shown = 0;
00808     eg->tree = NULL;
00809     gtk_tree_append(GTK_TREE(contact_list), eg->list_item);
00810 
00811     gtk_signal_connect(GTK_OBJECT(eg->list_item),  "button_press_event",
00812                GTK_SIGNAL_FUNC(group_click),
00813                (grouplist *)eg );
00814     gtk_drag_dest_set(eg->list_item, GTK_DEST_DEFAULT_ALL &
00815               ~GTK_DEST_DEFAULT_HIGHLIGHT,
00816               drag_types, 1,
00817               GDK_ACTION_MOVE|GDK_ACTION_DEFAULT);
00818     gtk_signal_connect(GTK_OBJECT(eg->list_item), "drag_motion",
00819                GTK_SIGNAL_FUNC(drag_motion_cb), eg);
00820     
00821     gtk_widget_show(eg->list_item);
00822 }
00823 
00824 /* makes an account visible on the buddy list, making the contact visible
00825    if necessary */
00826 void add_account_line( eb_account * ea)
00827 {
00828     GtkWidget * box, * label;
00829     
00830     if (ea->list_item)
00831         return;
00832     
00833     add_contact_line(ea->account_contact);
00834 
00835     ea->list_item = gtk_tree_item_new();
00836 
00837     box = gtk_hbox_new(FALSE, 1);
00838     ea->pix = gtk_pixmap_new(iconblank_pm, iconblank_bm);
00839     label = gtk_label_new(ea->handle);
00840     {
00841         char * c = g_strndup(RUN_SERVICE(ea)->get_status_string(ea), 20);
00842         if(strlen(c) == 20)
00843         {
00844             c[19] = c[18] = c[17] = '.';
00845             if(!status_tips)
00846             {
00847                 status_tips = gtk_tooltips_new();
00848             }
00849             /*
00850              * that 3rd parameter is not a bug, it really is a useless
00851              * parameter
00852              */
00853 
00854             gtk_tooltips_set_tip(GTK_TOOLTIPS(status_tips), ea->list_item,
00855                     RUN_SERVICE(ea)->get_status_string(ea),
00856                     _("status info here"));
00857         }
00858         ea->status = gtk_label_new(c);
00859         g_free(c);
00860     }
00861 
00862     gtk_box_pack_start(GTK_BOX(box), ea->pix, FALSE, FALSE, 1);
00863     gtk_widget_show(ea->pix);
00864     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
00865     gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 1);
00866     gtk_widget_show(label);
00867     gtk_box_pack_start(GTK_BOX(box), ea->status, FALSE, FALSE, 1);
00868     gtk_widget_show(ea->status);
00869 
00870     gtk_container_add(GTK_CONTAINER(ea->list_item), box);
00871     gtk_widget_show(box);
00872 
00873     ea->icon_handler = -1;
00874 
00875     gtk_object_set_user_data(GTK_OBJECT(ea->list_item), ea);
00876     gtk_tree_insert(GTK_TREE(ea->account_contact->tree), ea->list_item,
00877         get_account_position(ea));
00878 
00879     gtk_signal_connect(GTK_OBJECT(ea->list_item),  "button_press_event",
00880                   GTK_SIGNAL_FUNC(account_click),
00881                   (eb_account *)ea );
00882 
00883     gtk_widget_show(ea->list_item);
00884 
00885 }
00886 
00887 /* makes a contact visible on the buddy list */
00888 void add_contact_line( struct contact * ec)
00889 {
00890     GtkWidget * box;
00891     
00892     if (ec->list_item)
00893         return;
00894     
00895     ec->list_item = gtk_tree_item_new();
00896     ec->tree = gtk_tree_new();
00897 
00898     box = gtk_hbox_new(FALSE, 1);
00899     ec->pix = gtk_pixmap_new(iconblank_pm, iconblank_bm);
00900     ec->label = gtk_label_new(ec->nick);
00901     ec->status = gtk_label_new("");
00902 
00903     gtk_box_pack_start(GTK_BOX(box), ec->pix, FALSE, FALSE, 1);
00904     gtk_widget_show(ec->pix);
00905     gtk_misc_set_alignment(GTK_MISC(ec->label), 0.0, 0.5);
00906     gtk_box_pack_start(GTK_BOX(box), ec->label, TRUE, TRUE, 1);
00907     gtk_widget_show(ec->label);
00908     gtk_box_pack_start(GTK_BOX(box), ec->status, FALSE, FALSE, 1);
00909     gtk_widget_show(ec->status);
00910 
00911     gtk_container_add(GTK_CONTAINER(ec->list_item), box);
00912     gtk_widget_show(box);
00913 
00914     ec->icon_handler = -1;
00915 
00916     gtk_object_set_user_data(GTK_OBJECT(ec->list_item), ec);
00917     
00918     if (!ec->group->contacts_shown) {
00919         ec->group->tree = gtk_tree_new();
00920         gtk_tree_item_set_subtree(GTK_TREE_ITEM(ec->group->list_item),
00921             ec->group->tree);
00922         if(strcmp(_("Unknown"),ec->group->name) !=0 &&
00923            strcmp(_("Ignore"),ec->group->name) !=0)
00924             gtk_tree_item_expand(GTK_TREE_ITEM(ec->group->list_item));
00925     }
00926     gtk_drag_source_set(ec->list_item,
00927         GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
00928                 drag_types, 1,
00929         GDK_ACTION_MOVE|GDK_ACTION_DEFAULT);
00930     gtk_signal_connect(GTK_OBJECT(ec->list_item), "drag_begin",
00931                GTK_SIGNAL_FUNC(start_drag), ec);
00932     gtk_signal_connect(GTK_OBJECT(ec->list_item), "drag_data_get",
00933                GTK_SIGNAL_FUNC(drag_data_get),
00934                ec);
00935     
00936     ec->group->contacts_shown++;
00937     gtk_tree_insert(GTK_TREE(ec->group->tree), ec->list_item,
00938         get_contact_position(ec));
00939 
00940     gtk_tree_item_set_subtree(GTK_TREE_ITEM(ec->list_item), ec->tree);
00941     if(ec->expanded)
00942         gtk_tree_item_expand(GTK_TREE_ITEM(ec->list_item));
00943     else
00944         gtk_tree_item_collapse(GTK_TREE_ITEM(ec->list_item));
00945 
00946     gtk_signal_connect(GTK_OBJECT(ec->list_item),  "button_press_event",
00947                   GTK_SIGNAL_FUNC(contact_click),
00948                   (struct contact*)ec );
00949     gtk_signal_connect(GTK_OBJECT(ec->list_item), "expand",
00950                   GTK_SIGNAL_FUNC(expand_contact), (struct contact*)ec);
00951     gtk_signal_connect(GTK_OBJECT(ec->list_item), "collapse",
00952                   GTK_SIGNAL_FUNC(collapse_contact), (struct contact*)ec );
00953         
00954     gtk_widget_show(ec->list_item); 
00955 }
00956 
00957 /* hides a group on the buddy list */
00958 void remove_group_line( grouplist * eg)
00959 {
00960     GList * contacts;
00961     
00962     if (!eg->list_item)
00963         return;
00964         
00965     for (contacts = eg->members; contacts; contacts = contacts->next) {
00966         struct contact * ec = contacts->data;
00967         if (ec->list_item)
00968             remove_contact_line(ec);
00969     }
00970         
00971     gtk_container_remove(GTK_CONTAINER(contact_list), eg->list_item);
00972     eg->list_item = NULL;
00973     eg->tree = NULL;
00974     eg->label = NULL;
00975 }
00976 
00977 
00978 
00979 /* hides an account on the buddy list, hiding any child accounts in the
00980    process */
00981 void remove_contact_line( struct contact * ec)
00982 {
00983     GList * accounts;
00984     
00985     if (!ec->list_item)
00986         return;
00987         
00988     for (accounts = ec->accounts; accounts; accounts = accounts->next) {
00989         eb_account * ea = accounts->data;
00990         if (ea->list_item)
00991             remove_account_line(ea);
00992     }
00993     
00994     ec->group->contacts_shown--;
00995     gtk_container_remove(GTK_CONTAINER(ec->group->tree), ec->list_item);
00996     ec->list_item = NULL;
00997     ec->tree = NULL;
00998     ec->pix = NULL;
00999     ec->status = NULL;
01000     ec->label = NULL;
01001     if (ec->icon_handler != -1)
01002         gtk_timeout_remove(ec->icon_handler);
01003     ec->icon_handler = -1;
01004 }
01005 
01006 
01007 /* hides an account on the buddy list (removes it from the tree) */
01008 void remove_account_line( eb_account * ea)
01009 {
01010 
01011     if (!ea || !ea->account_contact || !ea->list_item) {
01012         if(!ea->account_contact)
01013             eb_debug(DBG_CORE, "Not removing account_line, ea->account_contact is NULL !\n");
01014         return;
01015     }
01016 
01017     gtk_container_remove(GTK_CONTAINER(ea->account_contact->tree), ea->list_item);
01018     ea->list_item = NULL;
01019     ea->pix = NULL;
01020     ea->status = NULL;
01021     if (ea->icon_handler != -1)
01022         gtk_timeout_remove(ea->icon_handler);
01023     ea->icon_handler = -1;
01024 }
01025 
01026 /* timeout function called 10 seconds after a contact logs off
01027    (removes the logoff icon and hides the contact if necessary) */
01028 gint hide_contact(struct contact * ec)
01029 {
01030     GList * l;
01031     
01032     if (ec->icon_handler == -1)
01033         return FALSE;
01034     ec->icon_handler = -1;
01035     
01036     if (status_show == 2)       
01037         remove_contact_line(ec);
01038     else
01039         for (l = ec->accounts; l; l = l->next) {
01040             eb_account * account = l->data;
01041             buddy_update_status(account);
01042         }
01043     
01044     return FALSE;
01045 }
01046 
01047 /* timeout function called 10 seconds after an account logs off
01048    (removes the logoff icon and hides the account if necessary)
01049    This timeout is only set when other accounts in the parent
01050    contact are remaining shown */
01051 static gint hide_account(eb_account * ea)
01052 {
01053     if (ea->icon_handler == -1)
01054         return FALSE;
01055     ea->icon_handler = -1;
01056             
01057     if (!ea->list_item)
01058         return FALSE;
01059             
01060     /* 
01061      * if status_show == 1 then we are in the Contacts Tab
01062      * If all account lines are removed from here, leaving
01063      * an empty contact, then the user cannot expand the 
01064      * contact to see the list of accounts.  While this isn't
01065      * such a big deal, it causes yattm to segfault when
01066      * you switch back to the Online view.
01067      *  - Philip
01068      */
01069     if ((status_show == 2) /*|| (status_show == 1)*/)
01070         remove_account_line(ea);
01071     else
01072         buddy_update_status(ea);
01073     return FALSE;
01074 }
01075 
01076 /* update the status info (pixmap and state) of a contact */
01077 void contact_update_status(struct contact * ec)
01078 {
01079     GdkPixmap * pm;
01080     GdkBitmap * bm;
01081     eb_account * ea = NULL;
01082     GList * l;
01083     int width, height;
01084     int width2, height2;
01085     int width3, height3;
01086 
01087     /* find the account who's status information should be reflected in
01088        the contact line (preferably the default protocol account, but
01089        if that one is not logged on, use another) */
01090     for (l = ec->accounts; l; l = l->next) 
01091     {
01092         eb_account * account = l->data;
01093         if (!ea)
01094             ea = account;
01095         if (!ea->online && account->online)
01096             ea = account;
01097         if (ec->default_chatb != ea->service_id && account->online &&
01098                 ec->default_chatb == account->service_id)
01099             ea = account;
01100     }
01101 
01102     if (!ea)
01103         return;
01104 
01105     {
01106         char * c = g_strndup(RUN_SERVICE(ea)->get_status_string(ea), 20);
01107         if(strlen(c) == 20)
01108         {
01109             c[19] = c[18] = c[17] = '.';
01110             if(!status_tips)
01111             {
01112                 status_tips = gtk_tooltips_new();
01113             }
01114             /*
01115              * that 3rd parameter is not a bug, it really is a useless
01116              * parameter
01117              */
01118 
01119             gtk_tooltips_set_tip(GTK_TOOLTIPS(status_tips), ec->list_item,
01120                     RUN_SERVICE(ea)->get_status_string(ea),
01121                     _("status info here"));
01122         }
01123         gtk_label_set_text(GTK_LABEL(ec->status), c);
01124         g_free(c);
01125     }
01126     gtk_label_set_text(GTK_LABEL(ec->label), ec->nick);
01127 
01128     /* set the icon if there isn't another timeout about to alter the icon */
01129     if (ec->icon_handler == -1) 
01130     {
01131         RUN_SERVICE(ea)->get_status_pixmap(ea, &pm, &bm);
01132         gtk_pixmap_set(GTK_PIXMAP(ec->pix), pm, bm);
01133     }
01134 
01135     width = contact_list->allocation.width;
01136     height = contact_list->allocation.height;
01137 
01138     if(GTK_WIDGET_VISIBLE(GTK_SCROLLED_WINDOW(contact_window)->vscrollbar))
01139     {
01140         width3 = GTK_SCROLLED_WINDOW(contact_window)->vscrollbar->allocation.width;
01141         height3 = GTK_SCROLLED_WINDOW(contact_window)->vscrollbar->allocation.height;
01142     }
01143     else
01144     {
01145         width3 = 0, height3 = 0;
01146     }
01147 
01148         if (do_noautoresize == 0)
01149     {
01150         width2 = contact_window->allocation.width;
01151         height2 = contact_window->allocation.height;
01152 
01153         if(width+width3> width2)
01154         {
01155             gtk_widget_set_usize(contact_window,width+width3+2,height2);
01156         } 
01157     }
01158     
01159 }
01160 
01161 /* called by a service module when a buddy's status changes
01162  * this will call buddy_update_status.  The service should
01163  * call this so that the change is written to the log file
01164  */
01165 void buddy_update_status_and_log(eb_account * ea)
01166 {
01167     eb_log_status_changed(ea, RUN_SERVICE(ea)->get_status_string(ea));
01168 
01169     buddy_update_status(ea);
01170 }
01171 
01172 /* update the status info (pixmap and state) of an account */
01173 void buddy_update_status(eb_account * ea)
01174 {
01175     GdkPixmap * pm;
01176     GdkBitmap * bm;
01177     if (!ea || !ea->list_item)
01178         return;
01179     
01180     {
01181         char * c = g_strndup(RUN_SERVICE(ea)->get_status_string(ea), 20);
01182         if(strlen(c) == 20)
01183         {
01184             c[19] = c[18] = c[17] = '.';
01185             if(!status_tips)
01186             {
01187                 status_tips = gtk_tooltips_new();
01188             }
01189             /*
01190              * that 3rd parameter is not a bug, it really is a useless
01191              * parameter
01192              */
01193 
01194             gtk_tooltips_set_tip(GTK_TOOLTIPS(status_tips), ea->list_item,
01195                     RUN_SERVICE(ea)->get_status_string(ea),
01196                     _("status info here"));
01197         }
01198         eb_update_status(ea, c);
01199         gtk_label_set_text(GTK_LABEL(ea->status), c);
01200 
01201         g_free(c);
01202     }
01203 
01204     /* update the icon if another timeout isn't about to change it */
01205     if (ea->icon_handler == -1) {
01206         RUN_SERVICE(ea)->get_status_pixmap(ea, &pm, &bm);
01207         gtk_pixmap_set(GTK_PIXMAP(ea->pix), pm, bm);
01208     }
01209 
01210     /* since the contact's status info  might be a copy of this
01211        account's status info, we should refresh that also */    
01212     contact_update_status(ea->account_contact); 
01213 }
01214 
01215 /* the timeout function called after a contact logs in (to take the
01216    "open door" icon away) */
01217 static gint set_contact_icon(struct contact * ec)
01218 {
01219     /* abort if another timeout already took care of it */
01220     if (ec->icon_handler == -1)
01221         return FALSE;
01222     ec->icon_handler = -1;
01223         
01224     contact_update_status(ec);
01225     return FALSE;
01226 }
01227 
01228 /* the timeout function called after an account logs in (to take the
01229    "open door" icon away) */
01230 static gint set_account_icon(eb_account * ea)
01231 {
01232     /* abort if another timeout already took care of it */
01233     if (ea->icon_handler == -1)
01234         return FALSE;
01235     ea->icon_handler = -1;
01236     
01237     /* do it here for pounce (else it's too soon) */
01238     if (ea->account_contact->online == 1)
01239         do_trigger_online(ea->account_contact);
01240 
01241     buddy_update_status(ea);
01242     return FALSE;
01243 }
01244 
01245 /* function called when a contact logs in */
01246 static void contact_login(struct contact * ec)
01247 {
01248     char buff[1024];
01249     ec->group->contacts_online++;
01250 
01251     /* display the "open door" icon */
01252     gtk_pixmap_set(GTK_PIXMAP(ec->pix), iconlogin_pm, iconlogin_bm);
01253     
01254     /* remove any other timeouts (if a user just logged out immediately before) */
01255     if (ec->icon_handler != -1)
01256         gtk_timeout_remove(ec->icon_handler);
01257         
01258     /* timeout to set the contact icon in 10 seconds */
01259     ec->icon_handler = gtk_timeout_add(10000, (GtkFunction)set_contact_icon,
01260         (gpointer) ec);
01261 
01262     if((time(NULL) - last_sound_played > 0) && do_online_sound)
01263     {
01264         /* If we have the do_no_sound_for_ignore flag set,
01265            only play the sound if the contact is not ignored. */
01266         if (!do_no_sound_for_ignore ||
01267         (strcasecmp(ec->group->name, _("Ignore")) != 0))
01268         {
01269             play_sound(BUDDY_ARRIVE);
01270             last_sound_played = time(NULL);
01271         }
01272     }
01273     
01274     eb_chat_window_do_timestamp(ec, 1);
01275     g_snprintf(buff, 1024, _("%s is now online"), ec->nick);
01276     update_status_message(buff);
01277 }
01278 
01279 /* function called when a contact logs off */
01280 static void contact_logoff(struct contact * ec)
01281 {
01282     char buff[1024];
01283     /* display the "closed door" icon */
01284     gtk_pixmap_set(GTK_PIXMAP(ec->pix), iconlogoff_pm, iconlogoff_bm);
01285     ec->group->contacts_online--;
01286     
01287     /* remove any other timeouts (if the user just logged in) */
01288     if (ec->icon_handler != -1)
01289         gtk_timeout_remove(ec->icon_handler);
01290         
01291     /* timeout to remove the contact from the list */
01292     ec->icon_handler = gtk_timeout_add(10000, (GtkFunction)hide_contact,
01293         (gpointer) ec);
01294 
01295     if((time(NULL) - last_sound_played > 0) && do_online_sound)
01296     {
01297         /* If we have the do_no_sound_for_ignore flag set,
01298            only play the sound if the contact is not ignored. */
01299         if (!do_no_sound_for_ignore ||
01300         (strcasecmp(ec->group->name, _("Ignore")) != 0))
01301         {
01302             play_sound(BUDDY_LEAVE);
01303             last_sound_played = time(NULL);
01304         }
01305     }
01306 
01307     do_trigger_offline(ec);
01308 
01309     eb_chat_window_do_timestamp(ec, 0);
01310     g_snprintf(buff, 1024, _("%s is now offline"), ec->nick);
01311     update_status_message(buff); 
01312 }
01313 
01314 /* timeout called every 30 seconds for each online account to update
01315    its status */
01316 static gint refresh_buddy_status(eb_account * ea)
01317 {
01318     /* remove the timeout if the account is no longer displayed */
01319     if (!ea->list_item || ea->status_handler == -1) {
01320         ea->status_handler = -1;
01321         return FALSE;
01322     }
01323     
01324     /* don't refresh if a "door" icon is being displayed */
01325     if (ea->icon_handler != -1)
01326         return TRUE;
01327         
01328     buddy_update_status(ea);
01329     return TRUE;
01330 }
01331 
01332 /* called to signify that a buddy has logged in, and updates the GUI
01333    accordingly */
01334 void buddy_login(eb_account * ea)
01335 {
01336     
01337     /* don't do anything if this has already been done */
01338     if (ea->online)
01339         return;
01340     
01341     ea->account_contact->online++;
01342     ea->online = TRUE;
01343     
01344     if (do_ignore_unknown && !strcmp(_("Unknown"), ea->account_contact->group->name))
01345         return;
01346     
01347     add_account_line(ea);
01348     
01349     /* sets the "open door" icon */
01350     gtk_pixmap_set(GTK_PIXMAP(ea->pix), iconlogin_pm, iconlogin_bm);
01351 
01352     /* set the timeout to remove the "open door" icon */
01353     if (ea->icon_handler != -1)
01354         gtk_timeout_remove(ea->icon_handler);
01355     ea->icon_handler = gtk_timeout_add(10000, (GtkFunction)set_account_icon,
01356         (gpointer) ea);
01357     
01358     /* if there is only one account (this one) logged in under the
01359        parent contact, we must login the contact also */
01360     if (ea->account_contact->online == 1)
01361         contact_login(ea->account_contact);
01362         
01363     buddy_update_status(ea);
01364     
01365     /* make sure the status gets updated often */
01366     ea->status_handler = gtk_timeout_add(30000,
01367         (GtkFunction)refresh_buddy_status, (gpointer) ea);
01368 }
01369 
01370 /* called to signify that a buddy has logged off, and updates the GUI
01371    accordingly */
01372 void buddy_logoff(eb_account * ea)
01373 {
01374     
01375     /* don't do anything if this has already been done */
01376     if (!ea || !ea->online)
01377         return;
01378     
01379     ea->account_contact->online--;
01380     ea->online = FALSE;
01381 
01382     if (do_ignore_unknown && !strcmp(_("Unknown"), ea->account_contact->group->name))
01383         return;
01384 
01385     /* sets the "closed door" icon */
01386     gtk_pixmap_set(GTK_PIXMAP(ea->pix), iconlogoff_pm, iconlogoff_bm);
01387 
01388     /* removes any previously set timeouts for the account */ 
01389     if (ea->icon_handler != -1)
01390         gtk_timeout_remove(ea->icon_handler);
01391     ea->icon_handler = -1;
01392     if (ea->status_handler != -1)
01393         gtk_timeout_remove(ea->status_handler);
01394     ea->status_handler = -1;
01395     
01396     /* if this is the last account of the parent contact to log off,
01397        we must log off the contact also */
01398     if (ea->account_contact->online == 0)
01399         contact_logoff(ea->account_contact);
01400 
01401     /* timeout to remove the "close door" icon */
01402     ea->icon_handler = gtk_timeout_add(10000, (GtkFunction)hide_account,
01403         (gpointer) ea);
01404 
01405 }
01406 
01407 void update_contact_window_length ()
01408 {
01409   int h,w;
01410   h = iGetLocalPref("length_contact_window");
01411   w = iGetLocalPref("width_contact_window");
01412   if (h == 0) 
01413       h = 256;
01414   if (w == 0)
01415       w = 150;
01416   eb_debug(DBG_CORE, "statuswindow size: %dx%d\n",h,w);
01417   gtk_widget_set_usize(contact_window, w, h);
01418 }
01419 
01420 /* Generates the contact list tree (should only be called once) */
01421 static GtkWidget* MakeContactList()
01422 {
01423     GList * l1;
01424     
01425     contact_window = gtk_scrolled_window_new(NULL, NULL);
01426     contact_list = gtk_tree_new();
01427 
01428     for( l1 = groups; l1; l1=l1->next )
01429     {
01430         grouplist * grp = l1->data;
01431         add_group_line(grp);
01432     }
01433     
01434     gtk_widget_show(contact_list);
01435     gtk_scrolled_window_add_with_viewport
01436       (GTK_SCROLLED_WINDOW(contact_window),
01437        contact_list);
01438     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(contact_window),
01439         GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
01440     update_contact_window_length ();
01441     return (contact_window);
01442 }
01443 
01444 
01445 
01446 GtkWidget* MakeStatusMenu(eb_local_account * ela)
01447 {
01448   GtkWidget* status_menu_item;
01449   GtkWidget* status_menu;
01450   GList * status_label;
01451   GList * temp_list;
01452   GtkWidget* hbox, *label;
01453   GtkStyle* style;
01454   GSList * group = NULL;
01455   GSList * widgets = NULL;
01456   int x;
01457   gchar string[255];
01458   status_menu = gtk_menu_new();
01459   style = gtk_widget_get_style(status_menu);
01460 
01461   assert(ela);
01462   gtk_widget_realize(status_menu);
01463   status_label = eb_services[ela->service_id].sc->get_states();
01464 
01465   status_menu_item = gtk_tearoff_menu_item_new();
01466   gtk_menu_append(GTK_MENU(status_menu), status_menu_item);
01467   gtk_widget_show(status_menu_item);
01468   
01469   for(temp_list = status_label, x = 0; temp_list; x++, temp_list=temp_list->next)
01470   {
01471       struct acctStatus * stats = g_new0(struct acctStatus, 1);
01472       stats->ela = ela;
01473       stats->status= x;
01474       status_menu_item = gtk_radio_menu_item_new(group);
01475       group = gtk_radio_menu_item_group(GTK_RADIO_MENU_ITEM(status_menu_item));
01476       widgets = g_slist_append(widgets, status_menu_item);
01477       hbox = gtk_hbox_new(FALSE, 3);
01478       label = gtk_label_new((gchar*)temp_list->data);
01479       gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
01480       gtk_widget_show(label);
01481       gtk_widget_show(hbox);
01482       gtk_widget_show(status_menu_item);
01483       gtk_container_add(GTK_CONTAINER(status_menu_item), hbox);
01484       gtk_menu_append(GTK_MENU(status_menu), status_menu_item);
01485       gtk_signal_connect(GTK_OBJECT(status_menu_item), "activate",
01486                          eb_status, (gpointer) stats );
01487       gtk_signal_connect(GTK_OBJECT(status_menu_item), "remove",
01488                          eb_status_remove, (gpointer) stats );
01489 
01490   }
01491 
01492   ela->status_menu = widgets;
01493 
01494   g_snprintf(string, 255, "%s [%s]", ela->handle, get_service_name(ela->service_id));
01495 
01496   ela->status_button = gtk_menu_item_new_with_label(string);
01497   
01498   /* The following doesn't work!  Why? */
01499   /*
01500   gtk_menu_set_active(GTK_MENU(status_menu), 
01501                    eb_services[ela->service_id].sc->get_current_state(ela) );
01502   */
01503 
01504   /* So why not use "gtk_check_menu_item_set_active"?  Well, that method
01505      emits a signal.  What was happening was that it would send a signal
01506      from the zeroth status item (usually "online") for some reason
01507      _and_ from the one we want.  It's as if the signal from the zeroth item
01508      was already in some queue.  Anyway, this would cause a status change to 
01509      online and then back again for each protocol.  This causes unpredictable 
01510      behavior when the user is offline. */
01511   
01512   /* First deactivate zeroth status radio item */
01513   GTK_CHECK_MENU_ITEM(g_slist_nth(widgets, 0)->data)->active = 0 ;
01514   /* Now, activate the desired status radio item */
01515   GTK_CHECK_MENU_ITEM(
01516     g_slist_nth(
01517                  widgets, 
01518                  eb_services[ela->service_id].sc->get_current_state(ela)
01519                )->data
01520   )->active = 1 ;
01521   
01522   gtk_menu_item_set_submenu(GTK_MENU_ITEM(ela->status_button), status_menu);
01523   
01524   g_list_free(status_label);
01525 
01526   return ela->status_button;
01527 }
01528 
01529 static void eb_import_function(GtkWidget *widget, gpointer callback) {
01530     void (*callback_function)();
01531 
01532     assert(callback);
01533     callback_function = callback;
01534     fprintf(stderr, _("eb_import_function: calling callback\n"));
01535     callback_function();
01536     update_contact_list ();
01537     write_contact_list();
01538 }
01539 
01540 static void eb_profile_function(GtkWidget *widget, gpointer data) {
01541     menu_item_data *mid=data;
01542 
01543     assert(data);
01544     fprintf(stderr, _("eb_profile_function: calling callback\n"));
01545     mid->callback(mid->user_data);
01546     update_contact_list ();
01547     write_contact_list();
01548 }
01549 
01550 
01551 void eb_profile_window(GtkWidget *profile_submenuitem)
01552 {
01553     GtkWidget *label;
01554     // import_items is a list of struct callback_items
01555     GList *list=NULL;
01556     GtkWidget * profile_menu = gtk_menu_new();
01557     menu_data *md=NULL;
01558     menu_item_data *mid=NULL;
01559     gboolean added = FALSE;
01560 
01561     label = gtk_tearoff_menu_item_new();
01562     gtk_menu_append(GTK_MENU(profile_menu), label);
01563     gtk_widget_show(label);
01564 
01565     eb_debug(DBG_CORE, ">\n");
01566     /* import_items is a list that is maintained as a pref, modified by calls to eb_add_menu_item */
01567     md = GetPref(EB_PROFILE_MENU);
01568     if(md) {
01569         for(list = md->menu_items; list; list  = g_list_next(list) ) {
01570             mid=(menu_item_data *)list->data;
01571             eb_debug(DBG_CORE, "adding profile item: %s\n", mid->label);
01572             label = gtk_menu_item_new_with_label(mid->label);
01573             gtk_menu_append(GTK_MENU(profile_menu), label);
01574             gtk_signal_connect(GTK_OBJECT(label), "activate",
01575                     eb_profile_function, mid);
01576             gtk_widget_show(label);  
01577             added = TRUE;
01578         }
01579     }
01580     gtk_widget_set_sensitive(profile_submenuitem, added);
01581     
01582     gtk_menu_item_set_submenu(GTK_MENU_ITEM(profile_submenuitem), profile_menu);
01583     gtk_widget_show(profile_menu);
01584     gtk_widget_show(profile_submenuitem);
01585     eb_debug(DBG_CORE, "<\n");
01586 }
01587 
01588 void eb_import_window(GtkWidget *import_submenuitem)
01589 {
01590     GtkWidget *label;
01591     // import_items is a list of struct callback_items
01592     GList *list=NULL;
01593     GtkWidget * import_menu = gtk_menu_new();
01594     menu_data *md=NULL;
01595     menu_item_data *mid=NULL;
01596 
01597     label = gtk_tearoff_menu_item_new();
01598     gtk_menu_append(GTK_MENU(import_menu), label);
01599     gtk_widget_show(label);
01600 
01601     eb_debug(DBG_CORE, ">\n");
01602     /* import_items is a list that is maintained as a pref, modified by calls to eb_add_menu_item */
01603     md = GetPref(EB_IMPORT_MENU);
01604     if(md) {
01605         for(list = md->menu_items; list; list  = g_list_next(list) ) {
01606             mid=(menu_item_data *)list->data;
01607             eb_debug(DBG_CORE, "adding import item: %s\n", mid->label);
01608             label = gtk_menu_item_new_with_label(mid->label);
01609             gtk_menu_append(GTK_MENU(import_menu), label);
01610             gtk_signal_connect(GTK_OBJECT(label), "activate",
01611                     eb_import_function, mid->callback);
01612             gtk_widget_show(label);  
01613         }
01614     }
01615     gtk_menu_item_set_submenu(GTK_MENU_ITEM(import_submenuitem), import_menu);
01616     gtk_widget_show(import_menu);
01617     gtk_widget_show(import_submenuitem);
01618     eb_debug(DBG_CORE, "<\n");
01619 }
01620 
01621 void eb_set_status_window(GtkWidget *set_status_submenuitem)
01622 {
01623     GtkWidget *label;
01624     GtkWidget * account_menu = gtk_menu_new();
01625     GList *list=NULL;
01626 
01627     label = gtk_tearoff_menu_item_new();
01628     gtk_menu_append(GTK_MENU(account_menu), label); 
01629     gtk_widget_show(label);
01630     for(list = accounts; list; list  = g_list_next(list) )
01631     {
01632         label = MakeStatusMenu(list->data);
01633         ((eb_local_account*)(list->data))->status_button = label;
01634         gtk_menu_append(GTK_MENU(account_menu), label);
01635         gtk_widget_show(label);
01636     }
01637     gtk_menu_item_set_submenu(GTK_MENU_ITEM(set_status_submenuitem), account_menu);
01638     gtk_widget_show(account_menu);
01639     gtk_widget_show(set_status_submenuitem);
01640 }
01641 
01642 static GtkItemFactoryEntry menu_items[] = {
01643   { N_("/_File"),           NULL,       NULL, 0, "<Branch>" },
01644   { N_("/File/_Import"),        NULL,       NULL, 0, NULL },
01645   { N_("/File/_Set profile"),   NULL,       NULL, 0, NULL },
01646   { N_("/File/_Set status"),    NULL,       NULL, 0, NULL },
01647   { N_("/File/---"),        NULL,         NULL, 0, "<Separator>" },
01648   { N_("/File/Sign o_n all"),   "<control>A", eb_sign_on_all, 0, NULL },
01649   { N_("/File/Sign o_ff all"),  "<control>F", eb_sign_off_all, 0, NULL },
01650   { N_("/File/---"),        NULL,         NULL, 0, "<Separator>" },
01651   { N_("/File/_Quit"),      "<control>Q", delete_event, 0, NULL },
01652   { N_("/_Tools"),          "NULL", NULL, 0, "<Branch>" },
01653   { N_("/Tools/_New group chat..."),NULL, launch_group_chat, 0, NULL },
01654   { N_("/Tools/_Set as away"),  NULL, NULL, 0, NULL },
01655   { N_("/Tools/---"),       NULL, NULL, 0, "<Separator>" },
01656   { N_("/Tools/_Add a contact account..."),     
01657                 NULL, add_callback, 0, NULL },
01658   { N_("/Tools/Add a _group..."),   NULL, add_group_callback, 0, NULL },
01659   { N_("/Tools/---"),       NULL,         NULL, 0, "<Separator>" },
01660   { N_("/Tools/_Preferences..."),   NULL, build_prefs_callback, 0, NULL },
01661   { N_("/Tools/_Edit accounts"),        
01662                 NULL, eb_edit_accounts, 0, NULL },
01663   { N_("/_Help"),           NULL, NULL, 0, "<Branch>" },
01664   { N_("/_Help/_About"),        NULL, show_about, 0, NULL }
01665 };
01666 
01667 static GtkItemFactory *main_menu_factory = NULL;
01668 
01669 static gchar *menu_translate(const gchar *path, gpointer data)
01670 {
01671     gchar *retval;
01672 
01673     retval = (gchar *)gettext(path);
01674 
01675     return retval;
01676 }
01677 
01678 static void get_main_menu( GtkWidget  *window,
01679                     GtkWidget **menubar )
01680 {
01681   GtkItemFactory *item_factory;
01682   GtkAccelGroup *accel_group;
01683   gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
01684 
01685   accel_group = gtk_accel_group_new ();
01686 
01687   item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", 
01688                                        accel_group);
01689   main_menu_factory = item_factory;
01690   gtk_item_factory_set_translate_func(item_factory, menu_translate,
01691                         NULL, NULL);
01692   
01693   gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);
01694 
01695   gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
01696 
01697   if (menubar)
01698     /* Finally, return the actual menu bar created by the item factory. */ 
01699     *menubar = gtk_item_factory_get_widget (item_factory, "<main>");
01700 }
01701 
01702 void eb_status_window()
01703 {
01704     GtkWidget *statusbox;
01705     GtkWidget *vbox;
01706     GtkWidget *label;
01707     GtkWidget *menubox;
01708     GtkWidget *menu;
01709     GtkWidget *submenuitem;
01710     GtkWidget *hbox;
01711     GtkAccelGroup *accel = NULL;
01712     char * userrc = NULL;
01713     int win_x, win_y, win_w, win_h;
01714     int flags;
01715 
01716     statuswindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
01717     accel = gtk_accel_group_new();
01718     gtk_window_add_accel_group( GTK_WINDOW(statuswindow),accel );
01719     /* The next line allows you to make the window smaller than the orig. size */
01720     gtk_window_set_policy(GTK_WINDOW(statuswindow), TRUE, TRUE, TRUE);
01721     gtk_widget_realize(statuswindow);
01722 
01723     iconlogin_pm = gdk_pixmap_create_from_xpm_d(statuswindow->window, &iconlogin_bm,
01724         NULL, (gchar **) login_icon_xpm);
01725     iconblank_pm = gdk_pixmap_create_from_xpm_d(statuswindow->window, &iconblank_bm,
01726         NULL, (gchar **) blank_icon_xpm);
01727     iconlogoff_pm = gdk_pixmap_create_from_xpm_d(statuswindow->window, &iconlogoff_bm,
01728         NULL, (gchar **) logoff_icon_xpm);
01729 
01730     /* handle geometry - ivey */
01731 
01732 #ifndef __MINGW32__
01733     if (geometry[0] != 0) { 
01734         flags = XParseGeometry(geometry, &win_x, &win_y, &win_w, &win_h);
01735         /* proper negative handling comes later... 
01736         if ((flags & XValue) && (flags & XNegative))
01737             *win_x = DisplayWidth(display, screenNum) - *win_x;
01738         if ((flags & YValue) && (flags & YNegative))
01739             *win_y = DisplayHeight(display, screenNum) - *win_y;
01740         */
01741         gtk_window_set_position(GTK_WINDOW(statuswindow), GTK_WIN_POS_NONE); 
01742         gtk_widget_set_uposition(statuswindow, win_x, win_y);
01743         gtk_widget_set_usize(statuswindow, win_w, win_h);
01744     }
01745 #endif
01746     statusbox = gtk_vbox_new(FALSE, 0);
01747 
01748     menubox = gtk_handle_box_new();
01749     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(menubox), GTK_POS_LEFT);
01750     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(menubox), GTK_POS_LEFT);
01751     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(menubox), GTK_SHADOW_NONE);
01752 
01753     userrc = g_strconcat(config_dir, G_DIR_SEPARATOR_S, "menurc", NULL);
01754     gtk_item_factory_parse_rc(userrc);
01755     g_free(userrc);
01756 
01757     get_main_menu(statuswindow, &menu);
01758     
01759     /* fill in branches */
01760     
01761     submenuitem = gtk_item_factory_get_widget(main_menu_factory, "/File/Import");
01762     eb_import_window(submenuitem);
01763     SetPref("widget::import_submenuitem", submenuitem);
01764     
01765     submenuitem = gtk_item_factory_get_widget(main_menu_factory, "/File/Set profile");
01766     eb_profile_window(submenuitem);
01767     SetPref("widget::profile_submenuitem", submenuitem);
01768     
01769     submenuitem = gtk_item_factory_get_widget(main_menu_factory, "/File/Set status");
01770     eb_set_status_window(submenuitem);
01771     SetPref("widget::set_status_submenuitem", submenuitem);
01772     
01773     away_menu = gtk_menu_new();
01774     load_away_messages();
01775     build_away_menu();
01776 
01777     submenuitem = gtk_item_factory_get_widget(main_menu_factory, "/Tools/Set as away");
01778     gtk_menu_item_set_submenu(GTK_MENU_ITEM(submenuitem), away_menu);
01779     gtk_widget_show(away_menu);
01780         
01781     gtk_container_add(GTK_CONTAINER(menubox), menu );
01782     gtk_widget_show(menu);
01783     gtk_box_pack_start(GTK_BOX(statusbox), menubox, FALSE, FALSE, 0 );
01784     gtk_widget_show(menubox);
01785 
01786     /*
01787      * Do the main status window
01788      */
01789 
01790     vbox = gtk_vbox_new(FALSE, 0);
01791     hbox = gtk_hbox_new(FALSE, 0);
01792 
01793     label = gtk_radio_button_new_with_label
01794       (NULL, _("Online"));
01795     gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 1);
01796     gtk_signal_connect(GTK_OBJECT(label), "clicked",
01797                GTK_SIGNAL_FUNC(status_show_callback),
01798                (gpointer) 2);
01799     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(label),TRUE);
01800     gtk_widget_show(label);
01801 
01802 
01803     label = gtk_radio_button_new_with_label
01804       (gtk_radio_button_group(GTK_RADIO_BUTTON(label)),
01805        _("Contacts"));
01806     gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 1);
01807     gtk_signal_connect(GTK_OBJECT(label), "clicked",
01808                GTK_SIGNAL_FUNC(status_show_callback),
01809                (gpointer) 1);
01810     gtk_widget_show(label);
01811 
01812 
01813     label = gtk_radio_button_new_with_label
01814       (gtk_radio_button_group(GTK_RADIO_BUTTON(label)),
01815        _("Accounts"));
01816     gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 1);
01817     gtk_signal_connect(GTK_OBJECT(label), "clicked",
01818                GTK_SIGNAL_FUNC(status_show_callback),
01819                (gpointer) 0);
01820     gtk_widget_show(label);
01821 
01822     gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,0);
01823     gtk_widget_show(hbox);
01824 
01825     eb_debug(DBG_CORE, "%d\n", g_list_length(accounts));
01826     MakeContactList();
01827     gtk_widget_show(contact_window);
01828     gtk_box_pack_start(GTK_BOX(vbox),contact_window,TRUE,TRUE,0);
01829 
01830     gtk_widget_show(vbox);
01831 
01832     gtk_box_pack_start(GTK_BOX(statusbox), vbox, TRUE, TRUE,0);
01833 
01834     /*
01835      * Status Bar
01836      */
01837 
01838     hbox = gtk_handle_box_new();
01839     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hbox), GTK_POS_LEFT);
01840     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hbox), GTK_POS_LEFT);
01841     status_message = gtk_label_new(_("Welcome To Yattm"));
01842     status_bar = gtk_frame_new(NULL);
01843     gtk_frame_set_shadow_type(GTK_FRAME(status_bar), GTK_SHADOW_IN );
01844     gtk_widget_show(status_message);
01845     gtk_container_add(GTK_CONTAINER(status_bar), status_message);
01846     gtk_widget_show(status_bar);
01847     gtk_container_add(GTK_CONTAINER(hbox), status_bar);
01848     gtk_widget_show(hbox);
01849         
01850         gtk_box_pack_start(GTK_BOX(statusbox), hbox ,FALSE, FALSE,0);
01851         gtk_window_set_title(GTK_WINDOW(statuswindow), _("Yattm "VERSION));
01852     eb_icon(statuswindow->window);
01853     gtk_widget_show(statusbox);
01854 
01855     gtk_container_add(GTK_CONTAINER(statuswindow), statusbox );
01856 
01857     gtk_signal_connect (GTK_OBJECT (statuswindow), "delete_event",
01858                 GTK_SIGNAL_FUNC (delete_event), NULL);
01859 
01860     gtk_widget_show( statuswindow);
01861 
01862     gtk_signal_connect(GTK_OBJECT(contact_window), "size_allocate",
01863             eb_save_size,NULL);
01864     update_contact_list ();
01865 }
01866 
01867 

Contact: Andy Maloney     [Documentation generated by doxygen]