X-Git-Url: http://cgit.babelmonkeys.de/?p=jubjub.git;a=blobdiff_plain;f=src%2Fgui%2Fgtk%2FJubGtkRosterUI.m;h=b3899e34a1d750edf1e57d0d6961d4a8f4baf326;hp=9a2da292a1f68075d0f02e176e2bf0c9efd9b4bb;hb=77e1d677232eeab371a8ac2da1e542bc62bbaea6;hpb=d5df5e6b07d60d4ac8f43213e719d3331a8b5eb6 diff --git a/src/gui/gtk/JubGtkRosterUI.m b/src/gui/gtk/JubGtkRosterUI.m index 9a2da29..b3899e3 100644 --- a/src/gui/gtk/JubGtkRosterUI.m +++ b/src/gui/gtk/JubGtkRosterUI.m @@ -1,6 +1,56 @@ #import "JubGtkRosterUI.h" #import "JubGObjectMap.h" #import "JubGtkChatUI.h" +#import "JubGtkHelper.h" + +#include + +static void roster_row_activated(GtkTreeView *tree_view, GtkTreePath *path, + GtkTreeViewColumn *column, gpointer data) +{ + JubGtkRosterUI *roster = data; + GtkTreeIter row_iter; + GtkTreeModel *tree_model; + gchar *jid_s; + XMPPJID *jid; + OFAutoreleasePool *pool; + + tree_model = gtk_tree_view_get_model(tree_view); + gtk_tree_model_get_iter(tree_model, &row_iter, path); + gtk_tree_model_get(tree_model, &row_iter, 1, &jid_s, -1); + + // This was a group row + if (!jid_s) return; + + pool = [OFAutoreleasePool new]; + jid = [XMPPJID JIDWithString: [OFString stringWithUTF8String: jid_s]]; + + [roster performSelectorOnMainThread: @selector(chatForJID:) + withObject: jid + waitUntilDone: NO]; + [pool release]; +} + +static void presence_changed(GtkComboBox *combo_box, gpointer data) +{ + XMPPPresence *pres; + XMPPConnection *connection = data; + OFAutoreleasePool *pool = [OFAutoreleasePool new]; + + const char *status = gtk_combo_box_get_active_id(combo_box); + + if (!strcmp(status, "unavailable")) + pres = [XMPPPresence presenceWithType: @"unavailable"]; + else { + pres = [XMPPPresence presence]; + if (strcmp(status, "available")) + [pres addShow: @(status)]; + } + + [connection sendStanza: pres]; + + [pool release]; +} static gboolean filter_roster_by_presence(GtkTreeModel *model, GtkTreeIter *iter, gpointer data) @@ -30,19 +80,31 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model, } @implementation JubGtkRosterUI -- initWithBuilder: (GtkBuilder*)builder_ +- initWithConnection: (XMPPConnection*)connection_ { self = [super init]; @try { + GtkTreeView *roster_view; + GtkComboBox *presence_combo; + GtkBuilder *builder; + groupMap = [[OFMapTable alloc] initWithKeyFunctions: keyFunctions valueFunctions: rowRefFunctions]; contactMap = [[OFMutableDictionary alloc] init]; chatMap = [[OFMutableDictionary alloc] init]; presences = [[OFCountedSet alloc] init]; + connection = [connection_ retain]; + + builder = gtk_builder_new(); + gtk_builder_add_from_file(builder, "data/gtk/roster.ui", NULL); + gtk_builder_connect_signals(builder, NULL); - builder = g_object_ref(builder_); + roster_window = + GTK_WIDGET(gtk_builder_get_object(builder, "RosterWindow")); + + gtk_widget_show(roster_window); roster_model = GTK_TREE_STORE(gtk_builder_get_object(builder, "RosterTreeStore")); @@ -52,6 +114,20 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model, gtk_tree_model_filter_set_visible_func(roster_filter, filter_roster_by_presence, presences, NULL); + + roster_view = GTK_TREE_VIEW(gtk_builder_get_object(builder, + "RosterTreeView")); + + g_signal_connect(roster_view, "row_activated", + G_CALLBACK(roster_row_activated), self); + + presence_combo = GTK_COMBO_BOX(gtk_builder_get_object(builder, + "PresenceComboBox")); + + g_signal_connect(presence_combo, "changed", + G_CALLBACK(presence_changed), connection); + + g_object_unref(G_OBJECT(builder)); } @catch (id e) { [self release]; @throw e; @@ -64,27 +140,16 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model, { [groupMap release]; [contactMap release]; + [chatMap release]; [presences release]; + [connection release]; - if (roster_model) - g_object_unref(roster_model); - - if (roster_filter) - g_object_unref(roster_filter); - - g_object_unref(builder); + gtk_widget_destroy(roster_window); [super dealloc]; } /* Presence handling */ -static gboolean refilter_roster(gpointer data) -{ - gtk_tree_model_filter_refilter(data); - - return FALSE; -} - - (void)connection: (XMPPConnection*)connection didReceivePresence: (XMPPPresence*)presence { @@ -93,147 +158,142 @@ static gboolean refilter_roster(gpointer data) else if ([presence.type isEqual: @"unavailable"]) [presences removeObject: [presence.from bareJID]]; - g_idle_add(refilter_roster, roster_filter); + g_idle_add_block(^{ + gtk_tree_model_filter_refilter(roster_filter); + }); } // FIXME: This needs to move somewhere else -- (void)connection: (XMPPConnection*)connection - didReceiveMessage: (XMPPMessage*)message +- (JubGtkChatUI*)chatForJID: (XMPPJID*)jid { - JubGtkChatUI *chat = [chatMap objectForKey: [message.from bareJID]]; + OFAutoreleasePool *pool = [OFAutoreleasePool new]; + JubGtkChatUI *chat = + [chatMap objectForKey: [jid bareJID]]; if (chat == nil) { OFString * title = [@"Chat with " stringByAppendingString: - [message.from bareJID]]; - chat = [JubGtkChatUI alloc]; - [[chat initWithTitle: title - sendBlock: ^(OFString *text) { - XMPPMessage *msg = - [XMPPMessage messageWithType: @"chat"]; - msg.to = message.from; - msg.body = text; - [connection sendStanza: msg]; - - [chat addMessage: msg.body - sender: [message.to bareJID]]; - }] autorelease]; + [jid bareJID]]; + + chat = [[[JubGtkChatUI alloc] + initWithTitle: title + closeBlock: ^{ + [chatMap removeObjectForKey: [jid bareJID]]; + } + sendBlock: ^(OFString *text) { + XMPPMessage *msg = + [XMPPMessage messageWithType: @"chat"]; + msg.to = jid; + msg.body = text; + [connection sendStanza: msg]; + } + ] autorelease]; [chatMap setObject: chat - forKey: [message.from bareJID]]; + forKey: [jid bareJID]]; } + + [pool release]; + + return chat; +} + +- (void)connection: (XMPPConnection*)connection + didReceiveMessage: (XMPPMessage*)message +{ + JubGtkChatUI *chat = [self chatForJID: message.from]; [chat addMessage: message.body sender: [message.from bareJID]]; } /* Roster Delegate methods */ -struct add_roster_item_param { - OFString *group; - OFString *name; - OFString *jid; - OFMapTable *groupMap; - OFMapTable *contactRows; - GtkTreeStore *roster_model; -}; - -static gboolean add_roster_item(gpointer user_data) +- (void)Jub_addRosterItem: (XMPPRosterItem*)item + group: (OFString*)group { - struct add_roster_item_param *params = user_data; - GtkTreeIter group_iter, contact_iter; - GtkTreeRowReference *group_ref, *contact_ref; - GtkTreePath *group_path, *contact_path; - - group_ref = [params->groupMap valueForKey: params->group]; - - if (!group_ref) { - // Create new group row - gtk_tree_store_append(params->roster_model, &group_iter, NULL); - gtk_tree_store_set(params->roster_model, &group_iter, - 0, [params->group UTF8String], -1); - - group_path = gtk_tree_model_get_path(GTK_TREE_MODEL( - params->roster_model), &group_iter); - - group_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL( - params->roster_model), group_path); - - [params->groupMap setValue: group_ref - forKey: params->group]; - } else { - // Get iter for existing group row - group_path = gtk_tree_row_reference_get_path(group_ref); - - gtk_tree_model_get_iter(GTK_TREE_MODEL(params->roster_model), - &group_iter, group_path); - } - gtk_tree_path_free(group_path); - - // Create new contact row - gtk_tree_store_append(params->roster_model, &contact_iter, &group_iter); - gtk_tree_store_set(params->roster_model, &contact_iter, - 0, [params->name UTF8String], 1, [params->jid UTF8String], -1); - - contact_path = gtk_tree_model_get_path(GTK_TREE_MODEL( - params->roster_model), &contact_iter); - - contact_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL( - params->roster_model), contact_path); + g_idle_add_block(^{ + GtkTreeIter group_iter, contact_iter; + GtkTreeRowReference *group_ref, *contact_ref; + GtkTreePath *group_path, *contact_path; + OFMapTable *contactRows = + [contactMap objectForKey: [item.JID bareJID]]; + + group_ref = [groupMap valueForKey: group]; + + if (!group_ref) { + // Create new group row + gtk_tree_store_append(roster_model, &group_iter, NULL); + gtk_tree_store_set(roster_model, &group_iter, + 0, [group UTF8String], -1); + + group_path = gtk_tree_model_get_path(GTK_TREE_MODEL( + roster_model), &group_iter); + + group_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL( + roster_model), group_path); + + [groupMap setValue: group_ref + forKey: group]; + } else { + // Get iter for existing group row + group_path = gtk_tree_row_reference_get_path(group_ref); + + gtk_tree_model_get_iter(GTK_TREE_MODEL(roster_model), + &group_iter, group_path); + } + gtk_tree_path_free(group_path); + + // Create new contact row + gtk_tree_store_append(roster_model, &contact_iter, &group_iter); + if (item.name) + gtk_tree_store_set(roster_model, &contact_iter, + 0, [item.name UTF8String], + 1, [[item.JID bareJID] UTF8String], -1); + else + gtk_tree_store_set(roster_model, &contact_iter, + 0, [item.JID.node UTF8String], + 1, [[item.JID bareJID] UTF8String], -1); - gtk_tree_path_free(contact_path); + contact_path = gtk_tree_model_get_path(GTK_TREE_MODEL( + roster_model), &contact_iter); - [params->contactRows setValue: contact_ref - forKey: params->group]; + contact_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL( + roster_model), contact_path); - [params->group release]; - [params->name release]; - [params->jid release]; - [params->groupMap release]; - [params->contactRows release]; - g_object_unref(params->roster_model); - free(params); + gtk_tree_path_free(contact_path); - return FALSE; + [contactRows setValue: contact_ref + forKey: group]; + }); } -struct remove_roster_item_param { - OFString *group; - OFMapTable *groupMap; - OFMapTable *contactRows; - GtkTreeStore *roster_model; -}; - -static gboolean remove_roster_item(gpointer user_data) +- (void)Jub_removeRosterItem: (XMPPRosterItem*)item + group: (OFString*)group { - struct remove_roster_item_param *params = user_data; - GtkTreeIter contact_iter, group_iter; - GtkTreePath *contact_path, *group_path; - GtkTreeRowReference *contact_ref, *group_ref; - - contact_ref = [params->contactRows valueForKey: params->group]; - contact_path = gtk_tree_row_reference_get_path(contact_ref); - gtk_tree_model_get_iter(GTK_TREE_MODEL(params->roster_model), - &contact_iter, contact_path); - - gtk_tree_store_remove(params->roster_model, &contact_iter); - - group_ref = [params->groupMap valueForKey: params->group]; - group_path = gtk_tree_row_reference_get_path(group_ref); - gtk_tree_model_get_iter(GTK_TREE_MODEL(params->roster_model), - &group_iter, group_path); - - if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(params->roster_model), - &group_iter)) { - gtk_tree_store_remove(params->roster_model, &group_iter); - [params->groupMap removeValueForKey: params->group]; - } + g_idle_add_block(^{ + GtkTreeIter contact_iter, group_iter; + GtkTreePath *contact_path, *group_path; + GtkTreeRowReference *contact_ref, *group_ref; + OFMapTable *contactRows = + [contactMap objectForKey: [item.JID bareJID]]; - gtk_tree_path_free(group_path); + contact_ref = [contactRows valueForKey: group]; + contact_path = gtk_tree_row_reference_get_path(contact_ref); + gtk_tree_model_get_iter(GTK_TREE_MODEL(roster_model), + &contact_iter, contact_path); - [params->group release]; - [params->groupMap release]; - [params->contactRows release]; - g_object_unref(params->roster_model); - free(params); + gtk_tree_store_remove(roster_model, &contact_iter); - return FALSE; + group_ref = [groupMap valueForKey: group]; + group_path = gtk_tree_row_reference_get_path(group_ref); + gtk_tree_model_get_iter(GTK_TREE_MODEL(roster_model), + &group_iter, group_path); + + if (!gtk_tree_model_iter_has_child(GTK_TREE_MODEL(roster_model), + &group_iter)) { + gtk_tree_store_remove(roster_model, &group_iter); + [groupMap removeValueForKey: group]; + } + + gtk_tree_path_free(group_path); + }); } - (void)rosterWasReceived: (XMPPRoster*)roster_ @@ -254,17 +314,9 @@ static gboolean remove_roster_item(gpointer user_data) else groups = @[@"General"]; - for (OFString *group in groups) { - struct add_roster_item_param *params = - malloc(sizeof(*params)); - params->group = [group retain]; - params->name = [item.name retain]; - params->jid = [bareJID retain]; - params->groupMap = [groupMap retain]; - params->contactRows = [contactRows retain]; - params->roster_model = g_object_ref(roster_model); - g_idle_add(add_roster_item, params); - } + for (OFString *group in groups) + [self Jub_addRosterItem: item + group: group]; }]; } @@ -281,16 +333,9 @@ static gboolean remove_roster_item(gpointer user_data) else groups = @[@"General"]; - for (OFString *group in groups) { - struct remove_roster_item_param *params = - malloc(sizeof(*params)); - params->group = [group retain]; - params->contactRows = [[contactMap objectForKey: - [oldItem.JID bareJID]] retain]; - params->groupMap = [groupMap retain]; - params->roster_model = g_object_ref(roster_model); - g_idle_add(remove_roster_item, params); - } + for (OFString *group in groups) + [self Jub_removeRosterItem: oldItem + group: group]; [contactMap removeObjectForKey: [item.JID bareJID]]; } @@ -308,17 +353,9 @@ static gboolean remove_roster_item(gpointer user_data) else groups = @[@"General"]; - for (OFString *group in groups) { - struct add_roster_item_param *params = - malloc(sizeof(*params)); - params->group = [group retain]; - params->name = [item.name retain]; - params->jid = [[item.JID bareJID] retain]; - params->groupMap = [groupMap retain]; - params->contactRows = [contactRows retain]; - params->roster_model = g_object_ref(roster_model); - g_idle_add(add_roster_item, params); - } + for (OFString *group in groups) + [self Jub_addRosterItem: item + group: group]; } }