/*
* 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 );
}