/* * PSIP - A lightweight GTK GUI for pjsip * (C) James Budiono 2011 * License: GNU GPL Version 3 */ #include #include #define MAIN_GUI_GLADE "psip.glade" #define IM_GUI_GLADE "psip_im.glade" typedef struct _psip_state_struct psip_state_struct; struct _psip_state_struct { //gui widgets GtkWidget *main_window, *progress_window, *progressbar; GtkListStore *buddylist; GtkTreeView *buddylistview; GtkDialog *confirm_dialog, *addbuddy_dialog, *error_dialog; GtkEntry *addbuddy_nickname_field, *addbuddy_address_field; }; psip_state_struct *psip_state; /* ====================== Utility functions ===================== */ /* all functions are not thread-safe, caller must call gdk_threads_enter */ /* === pulse progress bar until progress bar is hidden ===*/ gboolean pulse_progress_bar() { if (GTK_WIDGET_VISIBLE (psip_state->progress_window)) { gtk_progress_bar_pulse (GTK_PROGRESS_BAR (psip_state->progressbar)); } else return FALSE; } /* === set the online status of a buddy === */ void buddyview_set_online_status (GtkTreeIter *i, gboolean status) { if (status == TRUE) { gtk_list_store_set (psip_state->buddylist, i, 4, "gtk-yes", 2, status, -1); } else { gtk_list_store_set (psip_state->buddylist, i, 4, "gtk-no", 2, status, -1); } } /* === get the currently selected row from the buddylist === */ GtkTreeIter *buddyview_get_selected() { GtkTreeIter *i; i = g_slice_new(GtkTreeIter); if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (psip_state->buddylistview), NULL, i)) { return i; } else { g_slice_free(GtkTreeIter,i); return NULL; } } /* === add a new buddy === */ void buddylist_add (gchararray nick, gchararray address) { GtkTreeIter i; gtk_list_store_append (psip_state->buddylist, &i); gtk_list_store_set (psip_state->buddylist, &i, 0, nick, 1, address, 2, FALSE, 4, "gtk-no", -1); //stub: subscribe presence for newly-added users } /* ===================== Actions ====================== */ /* === stub: login/logoff action === */ gpointer login_logoff_action (gpointer p) { GtkToggleToolButton *b = (GtkToggleToolButton *)p; gboolean request; gboolean result; gdk_threads_enter(); request = gtk_toggle_tool_button_get_active(b); gdk_threads_leave(); g_usleep(5*G_USEC_PER_SEC); if (request == TRUE) { //stub: logon } else { //stub: logoff } result = request; //done, update UI now gdk_threads_enter(); if (result == TRUE ) { gtk_tool_button_set_label (GTK_TOOL_BUTTON(b),"Online"); gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(b), "gtk-yes"); gtk_widget_set_tooltip_text (GTK_WIDGET(b), "Click here to go offline."); } else { gtk_tool_button_set_label (GTK_TOOL_BUTTON(b),"Offline"); gtk_tool_button_set_stock_id (GTK_TOOL_BUTTON(b), "gtk-no"); gtk_widget_set_tooltip_text (GTK_WIDGET(b), "Click here to go online."); } gtk_widget_hide (GTK_WIDGET(psip_state->progress_window)); gdk_threads_leave (); } /*====================== Callbacks =======================*/ /*==== callback: online state toggled - go online or go offline ===*/ void on_online_toggled (GtkToggleToolButton *b, gpointer p) { if (gtk_toggle_tool_button_get_active(b) == TRUE) gtk_progress_bar_set_text(GTK_PROGRESS_BAR (psip_state->progressbar), "Connecting"); else gtk_progress_bar_set_text(GTK_PROGRESS_BAR (psip_state->progressbar), "Disconnecting"); gtk_widget_show (GTK_WIDGET (psip_state->progress_window)); gdk_threads_add_timeout (250,pulse_progress_bar,NULL); g_thread_create (login_logoff_action,b,FALSE,NULL); } /*==== callback: remove a buddy - confirm and then delete ===*/ void on_remove_buddy_clicked (GtkButton *b, gpointer p) { GtkTreeIter *i = buddyview_get_selected(); if (i) { g_object_set (G_OBJECT (psip_state->confirm_dialog), "text", "Removing a friend.", NULL); if (gtk_dialog_run(psip_state->confirm_dialog) == GTK_RESPONSE_YES) { gtk_list_store_remove (psip_state->buddylist, i); } gtk_widget_hide (GTK_WIDGET (psip_state->confirm_dialog)); g_slice_free (GtkTreeIter,i); } } /*==== callback: remove a buddy - confirm and then delete ===*/ void on_add_buddy_clicked (GtkButton *b, gpointer p) { // clear fields from previous invocation gtk_entry_set_text (psip_state->addbuddy_address_field,""); gtk_entry_set_text (psip_state->addbuddy_nickname_field,""); try_again: if (gtk_dialog_run(psip_state->addbuddy_dialog) == GTK_RESPONSE_OK) { gchararray nick = g_strstrip (g_strdup (gtk_entry_get_text(psip_state->addbuddy_nickname_field))); gchararray address = g_strstrip (g_strdup (gtk_entry_get_text(psip_state->addbuddy_address_field))); //g_print ("Adding nick: %s address: %s\n", nick, address ); if (strlen(nick) && strlen(address)) { buddylist_add (nick, address); g_free(nick); g_free(address); } else { g_object_set (G_OBJECT (psip_state->error_dialog), "text", "Both nick name and address must be filled-in.", NULL); gtk_dialog_run (psip_state->error_dialog); gtk_widget_hide (GTK_WIDGET (psip_state->error_dialog)); g_free(nick); g_free(address); goto try_again; } } gtk_widget_hide (GTK_WIDGET (psip_state->addbuddy_dialog)); } /*==== callback: message a buddy - open new message window ===*/ void on_message_buddy_clicked (GtkButton *b, gpointer p) { GtkBuilder *builder; GtkWidget *im_window; GError *error = NULL; /* Create new GtkBuilder object */ builder = gtk_builder_new(); /* Load UI from file. If error occurs, report it and quit application. */ if( ! gtk_builder_add_from_file( builder, IM_GUI_GLADE, &error ) ) { g_warning( "%s", error->message ); return; } /* Get main window pointer from UI */ im_window = GTK_WIDGET( gtk_builder_get_object( builder, "im_window" ) ); /* Connect signals */ gtk_builder_connect_signals( builder, NULL); /* Destroy builder, since we don't need it anymore */ g_object_unref( G_OBJECT( builder ) ); gtk_widget_show (im_window); } /* === test: set selected buddy to be online === */ void make_online (GtkButton *b, gpointer p) { GtkTreeIter *i = buddyview_get_selected(); if (i) { buddyview_set_online_status (i, TRUE); g_slice_free (GtkTreeIter,i); } } /* === test: set selected buddy to be offline === */ void make_offline (GtkButton *b, gpointer p) { GtkTreeIter *i = buddyview_get_selected(); if (i) { buddyview_set_online_status (i, FALSE); g_slice_free (GtkTreeIter, i); } } /* === test: populate buddylist === */ void populate_buddies() { int i; gchararray nick, address; for (i = 0; i < 10; i++) { nick = g_strdup_printf("user%d",i); address = g_strdup_printf("sip:user%d@iptel.org",i); buddylist_add (nick, address); g_free(nick); g_free(address); } gtk_list_store_clear(psip_state->buddylist); } /*========= main ======= */ int main( int argc, char **argv ) { GtkBuilder *builder; GtkWidget *window; GError *error = NULL; g_thread_init(NULL); gdk_threads_init(); /* allocate our data structure to hold applications states */ psip_state = g_slice_new(psip_state_struct); /* Init GTK+ */ gtk_init( &argc, &argv ); /* Create new GtkBuilder object */ builder = gtk_builder_new(); /* Load UI from file. If error occurs, report it and quit application. */ if( ! gtk_builder_add_from_file( builder, MAIN_GUI_GLADE, &error ) ) { g_warning( "%s", error->message ); return( 1 ); } /* Get main window pointer from UI */ window = GTK_WIDGET( gtk_builder_get_object( builder, "main_window" ) ); /* get other objects from UI */ psip_state->main_window = window; psip_state->progress_window = GTK_WIDGET (gtk_builder_get_object( builder, "progress_window" )); psip_state->progressbar = GTK_WIDGET (gtk_builder_get_object( builder, "pg1" )); psip_state->buddylist = GTK_LIST_STORE (gtk_builder_get_object( builder, "buddylist" )); psip_state->buddylistview = GTK_TREE_VIEW (gtk_builder_get_object( builder, "buddylistview" )); psip_state->confirm_dialog = GTK_DIALOG (gtk_builder_get_object( builder, "confirm_dialog" )); psip_state->error_dialog = GTK_DIALOG (gtk_builder_get_object( builder, "error_dialog" )); psip_state->addbuddy_dialog = GTK_DIALOG (gtk_builder_get_object( builder, "addbuddy_dialog" )); psip_state->addbuddy_nickname_field = GTK_ENTRY (gtk_builder_get_object( builder, "addbuddy_nickname" )); psip_state->addbuddy_address_field = GTK_ENTRY (gtk_builder_get_object( builder, "addbuddy_address" )); /* Connect signals */ gtk_builder_connect_signals( builder, NULL); /* Destroy builder, since we don't need it anymore */ g_object_unref( G_OBJECT( builder ) ); /* === test === */ populate_buddies(); /* Show window. All other widgets are automatically shown by GtkBuilder */ gtk_widget_show( window ); /* Start main loop */ gdk_threads_enter(); gtk_main(); gdk_threads_leave(); g_slice_free(psip_state_struct, psip_state); g_print( "Done.\n"); return( 0 ); }