diff options
Diffstat (limited to 'src/mainwindow.c')
-rw-r--r-- | src/mainwindow.c | 1383 |
1 files changed, 1383 insertions, 0 deletions
diff --git a/src/mainwindow.c b/src/mainwindow.c new file mode 100644 index 0000000..addb978 --- /dev/null +++ b/src/mainwindow.c @@ -0,0 +1,1383 @@ +/* + * mainwindow.c + * + * The main (contact list) window + * + * (c) 2002-2007 Thomas White <taw27@srcf.ucam.org> + * 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 <config.h> +#endif + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#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 = 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, "<control>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", "<control>O", NULL, ONLINE_NLN }, + { "StatusAWYAction", NULL, "_Away", "<control>A", NULL, ONLINE_AWY }, + { "StatusBSYAction", NULL, "_Busy", NULL, NULL, ONLINE_BSY }, + { "StatusBRBAction", NULL, "Be _Right Back", "<control>B", NULL, ONLINE_BRB }, + { "StatusPHNAction", NULL, "On The _Phone", NULL, NULL, ONLINE_PHN }, + { "StatusLUNAction", NULL, "Out To _Lunch", "<control>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, "<span foreground=\"#777777\" style=\"italic\">%s</span>", 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, "<span foreground=\"#777777\">%s</span>%s<span foreground=\"#777777\" style=\"italic\"> %s</span>", + 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); +} |