/* * mainwindow.c * * The main (contact list) window * * (c) 2002-2007 Thomas White * Part of TuxMessenger - GTK+-based MSN Messenger client * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 dated June, 1991. * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "options.h" #include "about.h" #include "prefswindow.h" #include "accountwindow.h" #include "mainwindow.h" #include "msnprotocol.h" #include "msngenerics.h" #include "routines.h" #include "sbsessions.h" #include "statusicons.h" #include "main.h" #include "debug.h" #include "listcache.h" #include "contactlist.h" #include "addcontact.h" #include "messagewindow.h" #include "error.h" #include "listcleanup.h" typedef enum { MAINWINDOW_STATE_UNDEFINED, MAINWINDOW_STATE_DISPATCH, MAINWINDOW_STATE_ONLINE } MainWindowState; typedef enum { LISTSORT_ACTIVITY, LISTSORT_ALPHA } ContactListSorting; static struct { /* Internal data */ MainWindowState state; /* A record of which state the window is in */ GtkUIManager *ui; /* UI manager */ int quitting; /* Whether or not to quit on disconnection. */ int was_menustatuschange; /* Locks to avoid comical looping */ int just_updating; /* when changing status. */ /* Common to both states */ GtkWidget *window; /* Overall window */ GtkWidget *swindow; /* Scrolled Window inside window */ GtkWidget *vbox; /* vbox inside swindow */ GtkWidget *top_hbox; /* hbox at top of window */ GtkWidget *bigvbox; /* vbox to put scrolled window and menu bar into */ /* For the dispatch state */ GtkWidget *signin_button; /* The Big Red Button */ char *signin_text; /* The text inside the big red button */ /* For the online state */ GtkWidget *online_vbox; /* vbox to hold online contacts plus "Online" heading */ GtkWidget *offline_vbox; /* vbox to hold offline contacts plus "Offline" heading */ GtkWidget *top_vbox; /* vbox to hold friendly name and CSM. */ GtkWidget *friendlyname; /* label to contain the user's current friendly name */ GtkWidget *fname_eventbox; /* eventbox to contain "friendlyname" */ GtkWidget *csm_entry; /* Text entry box when CSM is being changed. */ GtkWidget *csm; /* label to contain the user's current CSM */ GtkWidget *csm_eventbox; /* eventbox to contain "csm" */ GtkWidget *fname_entry; /* Text entry box when friendlyname is being changed. */ GtkWidget *setstatus; /* "Change online status" combo box */ GtkWidget *online_label; /* "Online" heading */ GtkWidget *offline_label; /* "Offline" heading */ GtkWidget *status_vbox; /* vbox to sort out size of status */ GtkActionGroup *action_group; char *menu_subject; } mainwindow = { MAINWINDOW_STATE_UNDEFINED, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /* Internally-called function to end the program */ static void mainwindow_destroyed() { gtk_exit(0); } /* Internally-called function to start the sign-in process */ static void mainwindow_signin() { if (mainwindow.state != MAINWINDOW_STATE_DISPATCH) { debug_print("MA: That'd just be silly.\n"); return; } mainwindow_setonline(); msnprotocol_connect("", 0); } /* Internally-called function to undo the effects of mainwindow_setonline */ static void mainwindow_unsetonline() { assert(mainwindow.state == MAINWINDOW_STATE_ONLINE); addcontact_closeall(); gtk_widget_destroy(mainwindow.setstatus); gtk_widget_destroy(mainwindow.online_label); gtk_widget_destroy(mainwindow.offline_label); gtk_widget_destroy(mainwindow.online_vbox); gtk_widget_destroy(mainwindow.offline_vbox); gtk_widget_destroy(mainwindow.status_vbox); if ( mainwindow.friendlyname != NULL ) { gtk_widget_destroy(mainwindow.friendlyname); } if ( mainwindow.fname_entry != NULL ) { gtk_widget_destroy(mainwindow.fname_entry); } gtk_widget_destroy(mainwindow.fname_eventbox); if ( mainwindow.csm != NULL ) { gtk_widget_destroy(mainwindow.csm); } if ( mainwindow.csm_entry != NULL ) { gtk_widget_destroy(mainwindow.csm_entry); } gtk_widget_destroy(mainwindow.csm_eventbox); gtk_widget_destroy(mainwindow.top_vbox); mainwindow.state = MAINWINDOW_STATE_UNDEFINED; if ( mainwindow.quitting ) { /* Bye Bye... */ gtk_widget_destroy(mainwindow.window); } } /* Internally-called function to undo the effects of mainwindow_setdispatch */ static void mainwindow_unsetdispatch() { assert(mainwindow.state == MAINWINDOW_STATE_DISPATCH); gtk_widget_destroy(mainwindow.signin_button); free(mainwindow.signin_text); mainwindow.state = MAINWINDOW_STATE_UNDEFINED; } static void mainwindow_fname_labelize() { gfloat yalign; gtk_widget_destroy(mainwindow.fname_entry); mainwindow.fname_entry = NULL; mainwindow.friendlyname = gtk_label_new(""); assert(mainwindow.friendlyname != NULL); gtk_misc_get_alignment(GTK_MISC(mainwindow.friendlyname), NULL, &yalign); gtk_misc_set_alignment(GTK_MISC(mainwindow.friendlyname), 0, yalign); mainwindow_setmfn(listcache_getmfn()); gtk_container_add(GTK_CONTAINER(mainwindow.fname_eventbox), mainwindow.friendlyname); gtk_widget_show(mainwindow.friendlyname); } static void mainwindow_csm_labelize() { gfloat yalign; char *csm; gtk_widget_destroy(mainwindow.csm_entry); mainwindow.csm_entry = NULL; mainwindow.csm = gtk_label_new(""); assert(mainwindow.csm != NULL); gtk_misc_get_alignment(GTK_MISC(mainwindow.csm), NULL, &yalign); gtk_misc_set_alignment(GTK_MISC(mainwindow.csm), 0, yalign); csm = listcache_getcsm(); mainwindow_setcsm(csm); free(csm); gtk_container_add(GTK_CONTAINER(mainwindow.csm_eventbox), mainwindow.csm); gtk_widget_show(mainwindow.csm); } static void mainwindow_fname_activate() { const char *new_mfn; assert(mainwindow.fname_entry != NULL); assert(mainwindow.friendlyname == NULL); new_mfn = gtk_entry_get_text(GTK_ENTRY(mainwindow.fname_entry)); debug_print("MA: New friendly name: %s\n", new_mfn); /* Switch back to a label... */ mainwindow_fname_labelize(); /* NOW change the name... currently the widget displays the old name, meaning it'll still be accurate if the name change fails. */ msnprotocol_setmfn(new_mfn); } static void mainwindow_csm_activate() { const char *new_csm; assert(mainwindow.csm_entry != NULL); assert(mainwindow.csm == NULL); new_csm = strdup(gtk_entry_get_text(GTK_ENTRY(mainwindow.csm_entry))); debug_print("MA: New CSM: %s\n", new_csm); msnprotocol_setcsm(new_csm); /* Switch back to a label... */ mainwindow_csm_labelize(); } static int mainwindow_fname_keypress(GtkWidget *widget, GdkEventKey *event) { if ( event->keyval == GDK_Escape ) { mainwindow_fname_labelize(); } return 0; } static int mainwindow_csm_keypress(GtkWidget *widget, GdkEventKey *event) { if ( event->keyval == GDK_Escape ) { mainwindow_csm_labelize(); } return 0; } void mainwindow_fname_startchange() { const char *old_mfn; char *mfn_decode; if ( mainwindow.fname_entry != NULL ) { /* Already editing! */ return; } if ( msnprotocol_signedin() != 1 ) { /* Not ready. */ return; } old_mfn = listcache_getmfn(); mfn_decode = routines_urldecode(old_mfn); /* Replace friendly name display with editable box. */ gtk_widget_destroy(mainwindow.friendlyname); mainwindow.friendlyname = NULL; mainwindow.fname_entry = gtk_entry_new(); assert(mainwindow.fname_entry != NULL); gtk_entry_set_width_chars(GTK_ENTRY(mainwindow.fname_entry), strlen(mfn_decode)); gtk_entry_set_text(GTK_ENTRY(mainwindow.fname_entry), mfn_decode); g_signal_connect(GTK_OBJECT(mainwindow.fname_entry), "activate", GTK_SIGNAL_FUNC(mainwindow_fname_activate), NULL); g_signal_connect(GTK_OBJECT(mainwindow.fname_entry), "key-press-event", GTK_SIGNAL_FUNC(mainwindow_fname_keypress), NULL); gtk_container_add(GTK_CONTAINER(mainwindow.fname_eventbox), mainwindow.fname_entry); gtk_widget_show(mainwindow.fname_entry); gtk_editable_set_position(GTK_EDITABLE(mainwindow.fname_entry), -1); gtk_widget_grab_focus(mainwindow.fname_entry); free(mfn_decode); } void mainwindow_csm_startchange() { char *old_csm; if ( mainwindow.csm_entry != NULL ) { /* Already editing! */ return; } if ( msnprotocol_signedin() != 1 ) { /* Not ready. */ return; } if ( !GTK_WIDGET_VISIBLE(mainwindow.csm_eventbox) ) { /* CSM bar is hidden... better fix that first... */ GtkAction *action; action = gtk_action_group_get_action(mainwindow.action_group, "ShowCSMAction"); if ( action != NULL ) { gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); gtk_widget_show(mainwindow.csm_eventbox); } else { debug_print("MA: Whoops! Couldn't find the Action to reveal the CSM widget.\n"); } } old_csm = listcache_getcsm_noentities(); /* Replace friendly name display with editable box. */ gtk_widget_destroy(mainwindow.csm); mainwindow.csm = NULL; mainwindow.csm_entry = gtk_entry_new(); assert(mainwindow.csm_entry != NULL); gtk_entry_set_width_chars(GTK_ENTRY(mainwindow.csm_entry), strlen(old_csm)); gtk_entry_set_text(GTK_ENTRY(mainwindow.csm_entry), old_csm); free(old_csm); g_signal_connect(GTK_OBJECT(mainwindow.csm_entry), "activate", GTK_SIGNAL_FUNC(mainwindow_csm_activate), NULL); g_signal_connect(GTK_OBJECT(mainwindow.csm_entry), "key-press-event", GTK_SIGNAL_FUNC(mainwindow_csm_keypress), NULL); gtk_container_add(GTK_CONTAINER(mainwindow.csm_eventbox), mainwindow.csm_entry); gtk_widget_show(mainwindow.csm_entry); gtk_editable_set_position(GTK_EDITABLE(mainwindow.csm_entry), -1); gtk_widget_grab_focus(mainwindow.csm_entry); } /* Entry point when local friendly name is clicked (to change it) */ static void mainwindow_fname_clicked(GtkWidget *widget, GdkEventButton *button) { if ( button->type != GDK_2BUTTON_PRESS ) { return; } mainwindow_fname_startchange(); } static void mainwindow_csm_clicked(GtkWidget *widget, GdkEventButton *button) { if ( button->type != GDK_2BUTTON_PRESS ) { return; } mainwindow_csm_startchange(); } /* Called by src/msnprotocol.c's CHG handler to update the window. Normally, this won't change anything because the user will just have set the same setting themself. The exception is at sign-in, when the initial setting does not come from a direct click on the combo box. */ void mainwindow_forcestatus(OnlineState status) { int newstatus; debug_print("MA: mainwindow_forcestatus: %i\n", status); /* Order of items in this list MUST match those in mainwindow_setonline() */ switch ( status ) { case ONLINE_NLN : newstatus = 0; break; case ONLINE_AWY : newstatus = 1; break; case ONLINE_BSY : newstatus = 2; break; case ONLINE_BRB : newstatus = 3; break; case ONLINE_PHN : newstatus = 4; break; case ONLINE_LUN : newstatus = 5; break; case ONLINE_HDN : newstatus = 6; break; default : newstatus = -1; break; /* Should never happen */ } gtk_combo_box_set_active(GTK_COMBO_BOX(mainwindow.setstatus), newstatus); } /* Combobox "changed" handler. */ static int mainwindow_setstatus() { int newstatus; OnlineState status; g_object_get(G_OBJECT(mainwindow.setstatus), "active", &newstatus, NULL); debug_print("MA: mainwindow_setstatus: %i\n", newstatus); /* Order of items in this list MUST match those in mainwindow_setonline() */ switch ( newstatus ) { case 0 : status = ONLINE_NLN; break; case 1 : status = ONLINE_AWY; break; case 2 : status = ONLINE_BSY; break; case 3 : status = ONLINE_BRB; break; case 4 : status = ONLINE_PHN; break; case 5 : status = ONLINE_LUN; break; case 6 : status = ONLINE_HDN; break; default : status = ONLINE_ERR; break; } msnprotocol_setstatus(status); /* If this DIDN'T originate from the menu, update the menu. */ if ( mainwindow.was_menustatuschange ) { debug_print("MA: Status change originated from menu.\n"); mainwindow.was_menustatuschange = 0; } else { GtkAction *action; debug_print("MA: Updating menu. \n"); switch ( status ) { case ONLINE_NLN : action = gtk_action_group_get_action(mainwindow.action_group, "StatusNLNAction"); break; case ONLINE_AWY : action = gtk_action_group_get_action(mainwindow.action_group, "StatusAWYAction"); break; case ONLINE_BSY : action = gtk_action_group_get_action(mainwindow.action_group, "StatusBSYAction"); break; case ONLINE_BRB : action = gtk_action_group_get_action(mainwindow.action_group, "StatusBRBAction"); break; case ONLINE_PHN : action = gtk_action_group_get_action(mainwindow.action_group, "StatusPHNAction"); break; case ONLINE_LUN : action = gtk_action_group_get_action(mainwindow.action_group, "StatusLUNAction"); break; case ONLINE_HDN : action = gtk_action_group_get_action(mainwindow.action_group, "StatusHDNAction"); break; default : action = gtk_action_group_get_action(mainwindow.action_group, "StatusNLNAction"); break; } mainwindow.just_updating = 1; gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE); } return FALSE; } /* Put the main window into its main "online" state */ void mainwindow_setonline() { gfloat yalign; GtkWidget *menuitem; char *csm; if (mainwindow.state == MAINWINDOW_STATE_DISPATCH) { mainwindow_unsetdispatch(); } else if (mainwindow.state == MAINWINDOW_STATE_ONLINE) { return; /* Nothing to do! */ } assert(mainwindow.state == MAINWINDOW_STATE_UNDEFINED); mainwindow.online_vbox = gtk_vbox_new(FALSE, 0); mainwindow.offline_vbox = gtk_vbox_new(FALSE, 0); mainwindow.top_vbox = gtk_vbox_new(FALSE, 0); mainwindow.status_vbox = gtk_vbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(mainwindow.top_hbox), mainwindow.top_vbox, TRUE, TRUE, 5); mainwindow.fname_eventbox = gtk_event_box_new(); assert(mainwindow.fname_eventbox != NULL); mainwindow.friendlyname = gtk_label_new("Connecting..."); gtk_widget_set_usize(GTK_WIDGET(mainwindow.friendlyname), 10, -1); assert(mainwindow.friendlyname != NULL); gtk_container_add(GTK_CONTAINER(mainwindow.fname_eventbox), mainwindow.friendlyname); gtk_box_pack_start(GTK_BOX(mainwindow.top_vbox), mainwindow.fname_eventbox, TRUE, TRUE, 5); gtk_misc_get_alignment(GTK_MISC(mainwindow.friendlyname), NULL, &yalign); gtk_misc_set_alignment(GTK_MISC(mainwindow.friendlyname), 0, yalign); g_signal_connect(GTK_OBJECT(mainwindow.fname_eventbox), "button-press-event", GTK_SIGNAL_FUNC(mainwindow_fname_clicked), NULL); mainwindow.csm_eventbox = gtk_event_box_new(); assert(mainwindow.csm_eventbox != NULL); mainwindow.csm = gtk_label_new(NULL); gtk_widget_set_usize(GTK_WIDGET(mainwindow.csm), 10, -1); assert(mainwindow.csm != NULL); gtk_container_add(GTK_CONTAINER(mainwindow.csm_eventbox), mainwindow.csm); gtk_box_pack_start(GTK_BOX(mainwindow.top_vbox), mainwindow.csm_eventbox, TRUE, TRUE, 5); gtk_misc_get_alignment(GTK_MISC(mainwindow.csm), NULL, &yalign); gtk_misc_set_alignment(GTK_MISC(mainwindow.csm), 0, yalign); g_signal_connect(GTK_OBJECT(mainwindow.csm_eventbox), "button-press-event", GTK_SIGNAL_FUNC(mainwindow_csm_clicked), NULL); csm = listcache_getcsm(); mainwindow_setcsm(csm); free(csm); mainwindow.setstatus = gtk_combo_box_new_text(); gtk_combo_box_append_text(GTK_COMBO_BOX(mainwindow.setstatus), "Online"); gtk_combo_box_append_text(GTK_COMBO_BOX(mainwindow.setstatus), "Away"); gtk_combo_box_append_text(GTK_COMBO_BOX(mainwindow.setstatus), "Busy"); gtk_combo_box_append_text(GTK_COMBO_BOX(mainwindow.setstatus), "Be Right Back"); gtk_combo_box_append_text(GTK_COMBO_BOX(mainwindow.setstatus), "On the Phone"); gtk_combo_box_append_text(GTK_COMBO_BOX(mainwindow.setstatus), "Out to Lunch"); gtk_combo_box_append_text(GTK_COMBO_BOX(mainwindow.setstatus), "Appear Offline"); gtk_signal_connect_object(GTK_OBJECT(mainwindow.setstatus), "changed", GTK_SIGNAL_FUNC(mainwindow_setstatus), NULL); gtk_box_pack_end(GTK_BOX(mainwindow.status_vbox), mainwindow.setstatus, TRUE, FALSE, 0); gtk_box_pack_end(GTK_BOX(mainwindow.top_hbox), mainwindow.status_vbox, FALSE, FALSE, 0); /* Add "Online" and "Offline" headings */ mainwindow.online_label = gtk_label_new("---- Online ----"); gtk_box_pack_start(GTK_BOX(mainwindow.online_vbox), mainwindow.online_label, FALSE, FALSE, 0); mainwindow.offline_label = gtk_label_new("---- Offline ----"); gtk_box_pack_start(GTK_BOX(mainwindow.offline_vbox), mainwindow.offline_label, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(mainwindow.vbox), mainwindow.online_vbox, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(mainwindow.vbox), mainwindow.offline_vbox, FALSE, FALSE, 0); gtk_widget_set_usize(GTK_WIDGET(mainwindow.offline_vbox), 10, -1); gtk_widget_set_usize(GTK_WIDGET(mainwindow.online_vbox), 10, -1); gtk_widget_set_usize(GTK_WIDGET(mainwindow.offline_label), 10, -1); gtk_widget_set_usize(GTK_WIDGET(mainwindow.online_label), 10, -1); gtk_widget_show(mainwindow.online_label); gtk_widget_show(mainwindow.offline_label); gtk_widget_show(mainwindow.online_vbox); if ( !options_hideoffline() ) { gtk_widget_show(mainwindow.offline_vbox); } gtk_widget_show(mainwindow.friendlyname); gtk_widget_show(mainwindow.setstatus); gtk_widget_show(mainwindow.fname_eventbox); if ( !options_hidecsm() ) { gtk_widget_show(mainwindow.csm_eventbox); } gtk_widget_show(mainwindow.csm); gtk_widget_show(mainwindow.top_vbox); gtk_widget_show(mainwindow.status_vbox); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/connection/sign_out"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/connection/sign_in"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/connection/account"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/view/show_offline"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/view/show_csm"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/status_nln"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/status_awy"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/status_bsy"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/status_brb"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/status_phn"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/status_lun"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/status_hdn"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/change_name"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/change_csm"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/change_avatar"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/tools/add_contact"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/tools/list_cleanup"); gtk_widget_set_sensitive(menuitem, TRUE); mainwindow.state = MAINWINDOW_STATE_ONLINE; } void mainwindow_kickdispatch() { const char *username; username = options_username(); if ( mainwindow.state != MAINWINDOW_STATE_DISPATCH ) { return; } if ( (strlen(username) != 0) && (strlen(options_password()) != 0) && (strlen(options_hostname()) != 0) ) { GtkWidget *menuitem; strcpy(mainwindow.signin_text, "Not signed in.\n\n"); strncat(mainwindow.signin_text, username, 200); mainwindow.signin_text[255] = '\0'; strcat(mainwindow.signin_text, "\n\nClick here to connect"); mainwindow.signin_text[255] = '\0'; gtk_label_set_text(GTK_LABEL(GTK_BIN(mainwindow.signin_button)->child), mainwindow.signin_text); gtk_widget_set_sensitive(mainwindow.signin_button, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/connection/sign_in"); gtk_widget_set_sensitive(menuitem, TRUE); } else { GtkWidget *menuitem; gtk_widget_set_sensitive(mainwindow.signin_button, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/connection/sign_in"); gtk_widget_set_sensitive(menuitem, FALSE); strcpy(mainwindow.signin_text, "Please configure your account details"); gtk_label_set_text(GTK_LABEL(GTK_BIN(mainwindow.signin_button)->child), mainwindow.signin_text); /* If this is being called from accountwindow_apply(), this won't re-open the already-open window. */ accountwindow_open(); } } /* Set the main window up ready for sign in */ void mainwindow_setdispatch() { GtkWidget *menuitem; if (mainwindow.state == MAINWINDOW_STATE_DISPATCH) { return; /* Nothing to do! */ } else if (mainwindow.state == MAINWINDOW_STATE_ONLINE) { mainwindow_unsetonline(); } assert(mainwindow.state == MAINWINDOW_STATE_UNDEFINED); /* The big "Sign In" button. */ mainwindow.signin_button = gtk_button_new_with_label(""); gtk_label_set_justify(GTK_LABEL(GTK_BIN(mainwindow.signin_button)->child), GTK_JUSTIFY_CENTER); gtk_signal_connect(GTK_OBJECT(mainwindow.signin_button), "clicked", GTK_SIGNAL_FUNC(mainwindow_signin), NULL); mainwindow.signin_text = malloc(256); assert(mainwindow.signin_text != NULL); gtk_box_pack_start(GTK_BOX(mainwindow.vbox), mainwindow.signin_button, TRUE, TRUE, 5); GTK_WIDGET_SET_FLAGS(mainwindow.signin_button, GTK_CAN_DEFAULT); gtk_widget_grab_default(mainwindow.signin_button); gtk_widget_grab_focus(mainwindow.signin_button); gtk_widget_show(mainwindow.signin_button); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/connection/sign_out"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/connection/sign_in"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/connection/account"); gtk_widget_set_sensitive(menuitem, TRUE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/view/show_offline"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/view/show_groups"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/view/show_csm"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/view/sort_alphabet"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/view/sort_activity"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/status_nln"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/status_awy"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/status_bsy"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/status_brb"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/status_phn"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/status_lun"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/status_hdn"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/change_name"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/change_csm"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/status/change_avatar"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/tools/add_contact"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/tools/blockallow_lists"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/tools/reverse_list"); gtk_widget_set_sensitive(menuitem, FALSE); menuitem = gtk_ui_manager_get_widget(mainwindow.ui, "/mainwindow/tools/list_cleanup"); gtk_widget_set_sensitive(menuitem, FALSE); /* This ensures that the subsequent change to ONLINE_HDN when signing in actually constitutes a change of the menu bar. */ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_action_group_get_action(mainwindow.action_group, "StatusNLNAction")), TRUE); mainwindow.state = MAINWINDOW_STATE_DISPATCH; mainwindow_kickdispatch(); } static void mainwindow_addui_callback(GtkUIManager *ui, GtkWidget *widget, GtkContainer *container) { gtk_box_pack_start(GTK_BOX(container), widget, FALSE, FALSE, 0); /* Enable overflow menu if this is a toolbar */ if ( GTK_IS_TOOLBAR(widget) ) { gtk_toolbar_set_show_arrow(GTK_TOOLBAR(widget), TRUE); } } static void mainwindow_connection_signout() { if ( !msnprotocol_disconnected() ) { msnprotocol_setstatus(ONLINE_FLN); } else { debug_print("MA: Try signing in first.\n"); } } static int mainwindow_confirm_quit_response(GtkWidget *widget, int response) { if ( response != GTK_RESPONSE_YES ) { gtk_widget_destroy(widget); return FALSE; } /* Sign out */ if ( msnprotocol_signedin() ) { mainwindow.quitting = 1; msnprotocol_setstatus(ONLINE_FLN); } else { gtk_widget_destroy(mainwindow.window); } return FALSE; } static int mainwindow_confirm_quit() { GtkWidget *window; window = gtk_message_dialog_new(GTK_WINDOW(mainwindow.window), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "Are you sure you want to quit TuxMessenger?"); g_signal_connect(GTK_DIALOG(window), "response", GTK_SIGNAL_FUNC(mainwindow_confirm_quit_response), NULL); gtk_widget_show_all(window); return TRUE; } static int mainwindow_connection_quit() { mainwindow_confirm_quit(); return FALSE; } static int mainwindow_togglecsm(GtkWidget *widget) { gboolean active; GtkAction *action; action = gtk_action_group_get_action(mainwindow.action_group, "ShowCSMAction"); assert(action != NULL); active = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)); if ( active ) { /* Show CSM */ gtk_widget_show(mainwindow.csm_eventbox); options_sethidecsm(FALSE); } else { /* Hide CSM - see also mainwindow_sethidecsm() above. */ if ( mainwindow.csm_entry != NULL ) { mainwindow_csm_labelize(); } gtk_widget_hide(mainwindow.csm_eventbox); options_sethidecsm(TRUE); } options_save(); return FALSE; } static int mainwindow_toggleoffline(GtkWidget *widget) { gboolean active; GtkAction *action; if ( !msnprotocol_signedin() ) { debug_print("MA: Can't change that before signing in...\n"); return FALSE; } action = gtk_action_group_get_action(mainwindow.action_group, "ShowOfflineAction"); assert(action != NULL); active = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)); if ( active ) { /* Show Offline Contacts */ gtk_widget_show(mainwindow.offline_vbox); options_sethideoffline(FALSE); } else { /* Hide Offline Contacts */ gtk_widget_hide(mainwindow.offline_vbox); options_sethideoffline(TRUE); } options_save(); return FALSE; } static int mainwindow_menubarstatuschange(GtkRadioAction *action, GtkRadioAction *current, gpointer data) { debug_print("MA: mainwindow_menubarstatuschange\n"); if ( !msnprotocol_signedin() ) { debug_print("MA: Not signed in. Ignoring fixup change.\n"); return FALSE; } if ( mainwindow.just_updating ) { mainwindow.just_updating = 0; debug_print("MA: Got callback for just updating menu bar.\n"); return FALSE; } debug_print("MA: Menu bar status change.\n"); mainwindow.was_menustatuschange = 1; mainwindow.just_updating = 0; mainwindow_forcestatus(gtk_radio_action_get_current_value(action)); return FALSE; } static gint mainwindow_deletecontact(GtkWidget *widget, gpointer data) { printf("MA: Chosen to delete '%s'\n", mainwindow.menu_subject); #if 0 GtkWidget *hbox; GtkWidget *vbox; GtkWidget *icon; AddContactWindow *item; item = malloc(sizeof(AddContactWindow)); item->window = gtk_dialog_new_with_buttons("Add New Contact", mainwindow_gtkwindow(), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); g_signal_connect(item->window, "response", G_CALLBACK(addcontact_response), item); g_signal_connect_swapped(item->window, "response", G_CALLBACK(gtk_widget_destroy), item->window); g_signal_connect(item->window, "destroy", G_CALLBACK(addcontact_destroyed), item); hbox = gtk_hbox_new(FALSE, 20); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(item->window)->vbox), hbox); icon = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_DIALOG); gtk_box_pack_start(GTK_BOX(hbox), icon, TRUE, TRUE, 0); vbox = gtk_vbox_new(FALSE, 5); gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new("Enter the Passport address of your new contact:"), TRUE, TRUE, 0); item->username_entry = gtk_entry_new(); gtk_box_pack_start(GTK_BOX(vbox), item->username_entry, TRUE, TRUE, 0); g_signal_connect(item->username_entry, "activate", G_CALLBACK(addcontact_activate), item); item->allow_toggle = gtk_check_button_new_with_label("Allow this new contact to message you."); gtk_box_pack_start(GTK_BOX(vbox), item->allow_toggle, TRUE, TRUE, 0); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item->allow_toggle), TRUE); gtk_container_set_border_width(GTK_CONTAINER(hbox), 10); gtk_widget_show_all(item->window); #endif return 0; } static void mainwindow_addmenubar() { GtkActionEntry entries[] = { { "ConnectionAction", NULL, "_Connection", NULL, NULL, NULL }, #ifdef HAVE_GTK_2_6_0 { "SignInAction", GTK_STOCK_CONNECT, "_Sign In", NULL, NULL, G_CALLBACK(mainwindow_signin) }, { "SignOutAction", GTK_STOCK_DISCONNECT, "Sign _Out", NULL, NULL, G_CALLBACK(mainwindow_connection_signout) }, { "AboutAction", GTK_STOCK_ABOUT, "_About TuxMessenger", NULL, NULL, G_CALLBACK(about_open) }, #else /* HAVE_GTK_2_6_0 */ { "SignInAction", NULL, "_Sign In", NULL, NULL, G_CALLBACK(mainwindow_signin) }, { "SignOutAction", NULL, "Sign _Out", NULL, NULL, G_CALLBACK(mainwindow_connection_signout) }, { "AboutAction", NULL, "_About TuxMessenger", NULL, NULL, G_CALLBACK(about_open) }, #endif /* HAVE_GTK_2_6_0 */ { "AccountAction", GTK_STOCK_PROPERTIES, "_Account Details...", NULL, NULL, G_CALLBACK(accountwindow_open) }, { "QuitAction", GTK_STOCK_QUIT, NULL, "Q", "Quit the application", G_CALLBACK(mainwindow_connection_quit) }, { "ViewAction", NULL, "_View", NULL, NULL, NULL }, { "StatusAction", NULL, "_Status", NULL, NULL, NULL }, { "ChangeNameAction", NULL, "Change Screen _Name...", NULL, NULL, G_CALLBACK(mainwindow_fname_startchange) }, { "ChangeCSMAction", NULL, "Change Custom _Message...", NULL, NULL, G_CALLBACK(mainwindow_csm_startchange) }, { "ChangeAvatarAction", NULL, "Change A_vatar...", NULL, NULL, NULL }, { "ToolsAction", NULL, "_Tools", NULL, NULL, NULL }, { "AddContactAction", GTK_STOCK_ADD, "_Add Contact...", NULL, NULL,G_CALLBACK(addcontact_open) }, { "BlockAllowAction", NULL, "_Block and Allow Lists", NULL, NULL, NULL }, { "ReverseAction", NULL, "_Reverse List", NULL, NULL, NULL }, { "PreferencesAction", GTK_STOCK_PREFERENCES, "_Preferences...", NULL, NULL, G_CALLBACK(prefswindow_open) }, { "ListCleanupAction", NULL, "_List Cleanup...", NULL, NULL, G_CALLBACK(listcleanup_open) }, { "HelpAction", NULL, "_Help", NULL, NULL, NULL }, { "ContactMessageAction", GTK_STOCK_NEW, "New _Message", NULL, NULL, NULL }, { "ContactSendFileAction", GTK_STOCK_OPEN, "Send _File", NULL, NULL, NULL }, { "ContactBlockAction", GTK_STOCK_NO, "_Block Contact", NULL, NULL, NULL }, { "ContactUnblockAction", GTK_STOCK_YES, "_Unblock Contact", NULL, NULL, NULL }, { "ContactDeleteAction", GTK_STOCK_DELETE, "_Delete Contact", NULL, NULL, G_CALLBACK(mainwindow_deletecontact) }, }; GtkToggleActionEntry toggles[] = { { "ShowCSMAction", NULL, "Show _Custom Status Message", NULL, NULL, G_CALLBACK(mainwindow_togglecsm), !options_hidecsm() }, { "ShowOfflineAction", NULL, "Show _Offline Contacts", NULL, NULL, G_CALLBACK(mainwindow_toggleoffline), !options_hideoffline() }, { "ShowGroupsAction", NULL, "Show Contact _Groups", NULL, NULL, NULL, FALSE }, }; GtkRadioActionEntry radios_status[] = { { "StatusNLNAction", NULL, "_Online", "O", NULL, ONLINE_NLN }, { "StatusAWYAction", NULL, "_Away", "A", NULL, ONLINE_AWY }, { "StatusBSYAction", NULL, "_Busy", NULL, NULL, ONLINE_BSY }, { "StatusBRBAction", NULL, "Be _Right Back", "B", NULL, ONLINE_BRB }, { "StatusPHNAction", NULL, "On The _Phone", NULL, NULL, ONLINE_PHN }, { "StatusLUNAction", NULL, "Out To _Lunch", "L", NULL, ONLINE_LUN }, { "StatusHDNAction", NULL, "Appear O_ffline", NULL, NULL, ONLINE_HDN }, }; GtkRadioActionEntry radios_sorting[] = { { "SortAlphaAction", GTK_STOCK_SORT_ASCENDING, "Sort Contacts _Alphabetically", NULL, NULL, LISTSORT_ALPHA }, { "SortActiveAction", NULL, "_Sort Contacts by Activity", NULL, NULL, LISTSORT_ACTIVITY }, }; guint n_entries = G_N_ELEMENTS(entries); guint n_toggles = G_N_ELEMENTS(toggles); guint n_radios_status = G_N_ELEMENTS(radios_status); guint n_radios_sorting = G_N_ELEMENTS(radios_sorting); GError *error = NULL; mainwindow.action_group = gtk_action_group_new("TuxMessenger"); gtk_action_group_add_actions(mainwindow.action_group, entries, n_entries, NULL); gtk_action_group_add_toggle_actions(mainwindow.action_group, toggles, n_toggles, NULL); gtk_action_group_add_radio_actions(mainwindow.action_group, radios_status, n_radios_status, ONLINE_NLN, G_CALLBACK(mainwindow_menubarstatuschange), NULL); gtk_action_group_add_radio_actions(mainwindow.action_group, radios_sorting, n_radios_sorting, LISTSORT_ACTIVITY, NULL, NULL); mainwindow.ui = gtk_ui_manager_new(); gtk_ui_manager_insert_action_group(mainwindow.ui, mainwindow.action_group, 0); g_signal_connect(mainwindow.ui, "add_widget", G_CALLBACK(mainwindow_addui_callback), mainwindow.bigvbox); gtk_widget_show(mainwindow.bigvbox); if ( gtk_ui_manager_add_ui_from_file(mainwindow.ui, DATADIR"/tuxmessenger/mainwindow.ui", &error) == 0 ) { /* :( */ debug_print("MA: Error loading main window menu bar: %s\n", error->message); exit(1); } gtk_window_add_accel_group(GTK_WINDOW(mainwindow.window), gtk_ui_manager_get_accel_group(mainwindow.ui)); gtk_ui_manager_ensure_update(mainwindow.ui); } /* Create and open the main window, in dispatch state */ void mainwindow_open() { /* Create the main window. */ mainwindow.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_signal_connect(GTK_OBJECT(mainwindow.window), "destroy", GTK_SIGNAL_FUNC(mainwindow_destroyed), NULL); mainwindow.bigvbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(mainwindow.window), mainwindow.bigvbox); /* Set the title and size. */ gtk_window_set_title(GTK_WINDOW(mainwindow.window), "TuxMessenger"); gtk_window_set_default_size(GTK_WINDOW(mainwindow.window), 200, 500); /* Add menu bar */ mainwindow_addmenubar(); /* Create a scrolled window to hold the contacts. */ mainwindow.swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(mainwindow.swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_box_pack_end(GTK_BOX(mainwindow.bigvbox), mainwindow.swindow, TRUE, TRUE, 0); /* Structure common to both states */ mainwindow.vbox = gtk_vbox_new(FALSE, 0); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(mainwindow.swindow), GTK_WIDGET(mainwindow.vbox)); mainwindow.top_hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(mainwindow.vbox), mainwindow.top_hbox, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(mainwindow.vbox), 6); g_signal_connect(G_OBJECT(mainwindow.window), "delete-event", GTK_SIGNAL_FUNC(mainwindow_confirm_quit), NULL); mainwindow.state = MAINWINDOW_STATE_UNDEFINED; mainwindow_setdispatch(); gtk_widget_show_all(mainwindow.window); } /* Trivial function to add an accelerator group to the window, e.g. for the "statusmenu" */ void mainwindow_addaccelgroup(GtkAccelGroup *accel_group) { gtk_window_add_accel_group(GTK_WINDOW(mainwindow.window), accel_group); } static char *mainwindow_decodestatus(OnlineState status) { if ( status == ONLINE_NLN ) { return ""; } else if ( status == ONLINE_AWY ) { return "[Away] "; } else if ( status == ONLINE_IDL ) { return "[Idle] "; } else if ( status == ONLINE_BSY ) { return "[Busy] "; } else if ( status == ONLINE_BRB ) { return "[BRB] "; } else if ( status == ONLINE_PHN ) { return "[Phone] "; } else if ( status == ONLINE_LUN ) { return "[Food] "; } return "[Whoops] "; } /* Called from src/msnprotocol.c to set the UI version of the local user's friendly name */ void mainwindow_setmfn(const char *new_friendlyname) { char *final_fname; char *decoded_fname; assert(new_friendlyname != NULL); assert(mainwindow.friendlyname != NULL); decoded_fname = routines_urldecode(new_friendlyname); final_fname = malloc(strlen(new_friendlyname) + 11); sprintf(final_fname, "––– %s", decoded_fname); gtk_label_set_text(GTK_LABEL(mainwindow.friendlyname), final_fname); free(final_fname); free(decoded_fname); } void mainwindow_setcsm(const char *new_csm) { char *csm_markup; char *new_csm_edited; assert(new_csm != NULL); assert(mainwindow.csm != NULL); csm_markup = malloc(strlen(new_csm)+50); new_csm_edited = routines_killtriangles(new_csm); sprintf(csm_markup, "%s", new_csm_edited); gtk_label_set_markup(GTK_LABEL(mainwindow.csm), csm_markup); free(new_csm_edited); free(csm_markup); } static void mainwindow_adjustmenu_popup(GtkWidget *adjust_button, char *username, guint button, guint time) { GtkWidget *menu; menu = gtk_ui_manager_get_widget(mainwindow.ui, "/ui/contact"); /* It is conceivable that neither of these would be true */ if ( contactlist_isonlist("BL", username) ) { GtkWidget *disable = gtk_ui_manager_get_widget(mainwindow.ui, "/ui/contact/block"); gtk_widget_set_sensitive(GTK_WIDGET(disable), FALSE); } if ( contactlist_isonlist("AL", username) ) { GtkWidget *disable = gtk_ui_manager_get_widget(mainwindow.ui, "/ui/contact/unblock"); gtk_widget_set_sensitive(GTK_WIDGET(disable), FALSE); } gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, button, time); mainwindow.menu_subject = username; } /* Contact button "event" */ static gint mainwindow_contact_event(GtkWidget *button, GdkEvent *event, char *username) { if ( event->type == GDK_BUTTON_PRESS ) { if ( ((GdkEventButton *)event)->button == 3 ) { mainwindow_adjustmenu_popup(button, username, ((GdkEventButton *)event)->button, ((GdkEventButton *)event)->time); } } return 0; } /* Contact button clicked */ static gint mainwindow_contact_clicked(GtkWidget *button, char *username) { if ( msnprotocol_status() == ONLINE_HDN ) { error_report("You can't send messages while you are Invisible!"); return 0; } messagewindow_create_if_none(username, NULL); return 0; } static void mainwindow_userdnd_get(GtkWidget *widget, GdkDragContext *drag_context, GtkSelectionData *seldata, guint info, guint time, unsigned char *username) { debug_print("MA: DnD: sending '%s'\n", username); gtk_selection_data_set(seldata, gdk_atom_intern("STRING", FALSE), 0, username, strlen((char *)username)+1); } /* Neat feature: drag one user onto another to create a three-way conversation with them both. */ static void mainwindow_userdnd_receive(GtkWidget *widget, GdkDragContext *drag_context, gint x, gint y, GtkSelectionData *seldata, guint info, guint time, char *username1) { char *username2; username2 = (char *)seldata->data; if ( msnprotocol_status() == ONLINE_HDN ) { error_report("You can't send messages while you are Invisible!"); return; } if ( strcmp(username1, username2) == 0 ) { debug_print("MA: Try just clicking...\n"); return; } debug_print("MA: Pairing '%s' and '%s'...\n", username1, username2); messagewindow_create(username1, sbsessions_create_threeway(username1, username2)); } /* Do the UI business part of adding a contact to the UI */ static void mainwindow_addcontactui(UIContact *uicontact, char *username, char *friendlyname, OnlineState status) { char *friendlynametext; char *friendlynametext2; char *final_fnametext; GtkTargetEntry targets[1]; char *csmtext; char *csmtext2; char *tooltips_string; char *csmtext_noentities; assert(uicontact->hbox == NULL); assert(uicontact->adjustbutton == NULL); assert(uicontact->button == NULL); assert(uicontact->label == NULL); assert(uicontact->eventbox == NULL); assert(uicontact->tooltips == NULL); assert(status != ONLINE_HDN); /* Doesn't happen for contacts */ /* Create the button */ uicontact->button = gtk_button_new(); assert(uicontact->button != NULL); GTK_WIDGET_UNSET_FLAGS(uicontact->button, GTK_CAN_FOCUS); gtk_button_set_relief(GTK_BUTTON(uicontact->button), GTK_RELIEF_NONE); uicontact->hbox = gtk_hbox_new(FALSE, 0); assert(uicontact->hbox != NULL); gtk_container_add(GTK_CONTAINER(uicontact->button), uicontact->hbox); uicontact->pixmap = statusicons_pixmap(status); gtk_box_pack_start(GTK_BOX(uicontact->hbox), uicontact->pixmap, FALSE, FALSE, 0); /* Label containing status, friendly name and CSM */ friendlynametext = routines_urldecode(friendlyname); friendlynametext2 = routines_killtriangles_and_ampersands(friendlynametext); if ( status != ONLINE_FLN ) { csmtext = contactlist_csm(username, 0); csmtext_noentities = contactlist_csm(username, 1); if ( csmtext == NULL ) { csmtext = strdup(""); } if ( csmtext_noentities == NULL ) { csmtext_noentities = strdup(""); } csmtext2 = routines_killtriangles(csmtext); /* Up to eight characters of status indicator (five letters, two brackets, one space), * 34 characters of status markup, 50 bytes of CSM markup, and one null terminator. */ final_fnametext = malloc(strlen(friendlynametext2) + strlen(csmtext2) + 8 + 34 + 50 + 1); sprintf(final_fnametext, "%s%s %s", mainwindow_decodestatus(status), friendlynametext2, csmtext2); tooltips_string = malloc(strlen(friendlynametext) + strlen(csmtext_noentities) + strlen(username) + 3); if ( strlen(csmtext_noentities) > 0 ) { sprintf(tooltips_string, "%s\n%s\n%s", friendlynametext, username, csmtext_noentities); } else { sprintf(tooltips_string, "%s\n%s", friendlynametext, username); } free(csmtext_noentities); free(friendlynametext); free(friendlynametext2); free(csmtext); uicontact->label = gtk_label_new(friendlynametext); gtk_label_set_markup(GTK_LABEL(uicontact->label), final_fnametext); gtk_misc_set_alignment(GTK_MISC(uicontact->label), 0, 0.5); assert(uicontact->label != NULL); free(final_fnametext); free(csmtext2); g_signal_connect(uicontact->button, "clicked", GTK_SIGNAL_FUNC(mainwindow_contact_clicked), (gpointer)username); } else { friendlynametext = routines_urldecode(friendlyname); uicontact->label = gtk_label_new(friendlynametext); assert(uicontact->label != NULL); tooltips_string = malloc(strlen(friendlynametext) + strlen(username) + 2); sprintf(tooltips_string, "%s\n%s", friendlynametext, username); free(friendlynametext); gtk_misc_set_alignment(GTK_MISC(uicontact->label), 0, 0.5); } gtk_box_pack_start(GTK_BOX(uicontact->hbox), uicontact->label, TRUE, TRUE, 3); /* Packing and signal handling */ if ( status != ONLINE_FLN ) { gtk_box_pack_end(GTK_BOX(mainwindow.online_vbox), uicontact->button, TRUE, FALSE, 0); } else { gtk_box_pack_end(GTK_BOX(mainwindow.offline_vbox), uicontact->button, TRUE, FALSE, 0); } g_signal_connect(uicontact->button, "event", GTK_SIGNAL_FUNC(mainwindow_contact_event), (gpointer)username); /* Tooltips */ uicontact->tooltips = gtk_tooltips_new(); assert(uicontact->tooltips != NULL); gtk_tooltips_set_tip(uicontact->tooltips, uicontact->button, tooltips_string, NULL); free(tooltips_string); /* Drag and Drop */ targets[0].target = "tm_username"; targets[0].flags = GTK_TARGET_SAME_APP; targets[0].info = 1; gtk_drag_source_set(uicontact->button, GDK_BUTTON1_MASK, targets, 1, GDK_ACTION_COPY); // gtk_drag_source_set_icon_pixbuf(uicontact->button, ); g_signal_connect(uicontact->button, "drag-data-get", GTK_SIGNAL_FUNC(mainwindow_userdnd_get), (gpointer)username); /* Each button is also a DnD recipient... for pairing users in three-way conversations.. */ gtk_drag_dest_set(uicontact->button, GTK_DEST_DEFAULT_ALL, targets, 1, GDK_ACTION_COPY); g_signal_connect(uicontact->button, "drag-data-received", GTK_SIGNAL_FUNC(mainwindow_userdnd_receive), (gpointer)username); /* Sort out the size of the new item */ gtk_widget_set_usize(uicontact->hbox, 10, -1); gtk_widget_show_all(uicontact->button); } /* Called from src/contactlist.c to add a contact (on the FL) to the list UI * This function does the extra work of creating the UIContact structure. */ UIContact *mainwindow_addcontact(char *username, char *friendlyname, OnlineState status) { UIContact *uicontact; uicontact = malloc(sizeof(UIContact)); assert(uicontact != NULL); uicontact->hbox = NULL; uicontact->adjustbutton = NULL; uicontact->button = NULL; uicontact->label = NULL; uicontact->eventbox = NULL; uicontact->tooltips = NULL; uicontact->pixmap = NULL; mainwindow_addcontactui(uicontact, username, friendlyname, status); return uicontact; } /* Set a contact's online status to "status" */ void mainwindow_setcontactstatus(UIContact *uicontact, char *username, char *friendlyname, OnlineState status) { debug_print("MA: Setting contact status for %s.\n", username); mainwindow_removecontact(uicontact); mainwindow_addcontactui(uicontact, username, friendlyname, status); } void mainwindow_destroycontact(UIContact *uicontact) { /* This assumes the contact widgets have already been destroyed */ free(uicontact); } /* Remove the UI parts of a contact (but leave the data structure intact) */ void mainwindow_removecontact(UIContact *uicontact) { debug_print("MA: Removing a UIContact\n"); if ( uicontact->button != NULL ) { gtk_widget_destroy(uicontact->button); } uicontact->eventbox = NULL; uicontact->hbox = NULL; uicontact->adjustbutton = NULL; uicontact->label = NULL; uicontact->tooltips = NULL; uicontact->button = NULL; uicontact->pixmap = NULL; } /* Pass bits of information to src/pixmap.c so it can do its stuff */ GtkStyle *mainwindow_style() { return gtk_widget_get_style(mainwindow.window); } GdkWindow *mainwindow_window() { return mainwindow.window->window; } GtkWindow *mainwindow_gtkwindow() { return GTK_WINDOW(mainwindow.window); }