aboutsummaryrefslogtreecommitdiff
path: root/src/mainwindow.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mainwindow.c')
-rw-r--r--src/mainwindow.c1383
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);
+}