PSIP
Artifact [63f0dc9e15]
Not logged in

Artifact 63f0dc9e1579ea1a8a73dd43d7e983303e75ac4d:


/*
 * PSIP - A lightweight GTK GUI for pjsip
 * (C) James Budiono 2011
 * License: GNU GPL Version 3
 */
 
#include <string.h>
#include <gtk/gtk.h>

#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 );
}