X-Git-Url: http://cgit.babelmonkeys.de/?a=blobdiff_plain;f=src%2Fgui%2Fgtk%2FJubGtkRosterUI.m;h=bc1b36894b0a7ec4026d641105c28d0e69ce4ac9;hb=b1cb5e20d49fdb3500fd76ea18dcbcc77bbba8ee;hp=66a050ef4002b152a0fc7362542a4999da765e8a;hpb=2bf25c7ceca5ebeaaedead71af159d43cc025a23;p=jubjub.git diff --git a/src/gui/gtk/JubGtkRosterUI.m b/src/gui/gtk/JubGtkRosterUI.m index 66a050e..bc1b368 100644 --- a/src/gui/gtk/JubGtkRosterUI.m +++ b/src/gui/gtk/JubGtkRosterUI.m @@ -1,10 +1,11 @@ +#import +#include + #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) { @@ -44,7 +45,7 @@ static void presence_changed(GtkComboBox *combo_box, gpointer data) else { pres = [XMPPPresence presence]; if (strcmp(status, "available")) - [pres addShow: @(status)]; + [pres setShow: @(status)]; } [connection sendStanza: pres]; @@ -55,49 +56,45 @@ static void presence_changed(GtkComboBox *combo_box, gpointer data) static gboolean filter_roster_by_presence(GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { - char *jid_s; - OFString *jid; - OFCountedSet *presences = data; + char *status; + gtk_tree_model_get(model, iter, 2, &status, -1); - gtk_tree_model_get(model, iter, 1, &jid_s, -1); - - // Groups have no JID - if (!jid_s) + // Groups have no status + if (!status) return TRUE; - jid = [[OFString alloc] initWithUTF8String: jid_s]; - - g_free(jid_s); - - int num = [presences countForObject: jid]; - if (num) { - [jid release]; - return TRUE; - } else { - [jid release]; + if (!strcmp(status, "unavailable")) return FALSE; - } + + return TRUE; } @implementation JubGtkRosterUI -- initWithBuilder: (GtkBuilder*)builder_ - connection: (XMPPConnection*)connection_ +- initWithClient: (JubChatClient*)client_ { 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]; + client = [client_ retain]; + + [client.contactManager addDelegate: self]; - builder = g_object_ref(builder_); + builder = gtk_builder_new(); + gtk_builder_add_from_file(builder, "data/gtk/roster.ui", NULL); + gtk_builder_connect_signals(builder, NULL); + + 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")); @@ -106,7 +103,7 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model, gtk_builder_get_object(builder, "RosterTreeModelFilter")); gtk_tree_model_filter_set_visible_func(roster_filter, - filter_roster_by_presence, presences, NULL); + filter_roster_by_presence, NULL, NULL); roster_view = GTK_TREE_VIEW(gtk_builder_get_object(builder, "RosterTreeView")); @@ -117,8 +114,11 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model, presence_combo = GTK_COMBO_BOX(gtk_builder_get_object(builder, "PresenceComboBox")); - g_signal_connect(presence_combo, "changed", - G_CALLBACK(presence_changed), connection); + presence_combo_changed_handler_id = + g_signal_connect(presence_combo, "changed", + G_CALLBACK(presence_changed), client.connection); + + g_object_unref(G_OBJECT(builder)); } @catch (id e) { [self release]; @throw e; @@ -129,36 +129,17 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model, - (void)dealloc { + [client.contactManager removeDelegate: self]; [groupMap release]; [contactMap release]; - [presences release]; - [connection release]; + [chatMap release]; + [client 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 */ -- (void)connection: (XMPPConnection*)connection - didReceivePresence: (XMPPPresence*)presence -{ - if ([presence.type isEqual: @"available"]) - [presences addObject: [presence.from bareJID]]; - else if ([presence.type isEqual: @"unavailable"]) - [presences removeObject: [presence.from bareJID]]; - - g_idle_add_block(^{ - gtk_tree_model_filter_refilter(roster_filter); - }); -} - // FIXME: This needs to move somewhere else - (JubGtkChatUI*)chatForJID: (XMPPJID*)jid { @@ -179,7 +160,7 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model, [XMPPMessage messageWithType: @"chat"]; msg.to = jid; msg.body = text; - [connection sendStanza: msg]; + [client.connection sendStanza: msg]; } ] autorelease]; @@ -192,9 +173,12 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model, return chat; } -- (void)connection: (XMPPConnection*)connection - didReceiveMessage: (XMPPMessage*)message +- (void)contact: (XMPPContact*)contact + didSendMessage: (XMPPMessage*)message { + if (message.body == nil || ![message.type isEqual: @"chat"]) + return; + JubGtkChatUI *chat = [self chatForJID: message.from]; [chat addMessage: message.body sender: [message.from bareJID]]; @@ -208,8 +192,17 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model, GtkTreeIter group_iter, contact_iter; GtkTreeRowReference *group_ref, *contact_ref; GtkTreePath *group_path, *contact_path; - OFMapTable *contactRows = - [contactMap objectForKey: [item.JID bareJID]]; + OFString *bareJID = [item.JID bareJID]; + OFMapTable *contactRows; + + if (!(contactRows = [contactMap objectForKey: bareJID])) { + contactRows = [OFMapTable + mapTableWithKeyFunctions: keyFunctions + valueFunctions: rowRefFunctions]; + + [contactMap setObject: contactRows + forKey: bareJID]; + } group_ref = [groupMap valueForKey: group]; @@ -241,11 +234,13 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model, if (item.name) gtk_tree_store_set(roster_model, &contact_iter, 0, [item.name UTF8String], - 1, [[item.JID bareJID] UTF8String], -1); + 1, [bareJID UTF8String], + 2, "unavailable", -1); else gtk_tree_store_set(roster_model, &contact_iter, 0, [item.JID.node UTF8String], - 1, [[item.JID bareJID] UTF8String], -1); + 1, [bareJID UTF8String], + 2, "unavailable", -1); contact_path = gtk_tree_model_get_path(GTK_TREE_MODEL( roster_model), &contact_iter); @@ -267,13 +262,14 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model, GtkTreeIter contact_iter, group_iter; GtkTreePath *contact_path, *group_path; GtkTreeRowReference *contact_ref, *group_ref; - OFMapTable *contactRows = - [contactMap objectForKey: [item.JID bareJID]]; + OFString *bareJID = [item.JID bareJID]; + OFMapTable *contactRows = [contactMap objectForKey: bareJID]; 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); + gtk_tree_path_free(contact_path); gtk_tree_store_remove(roster_model, &contact_iter); @@ -289,70 +285,135 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model, } gtk_tree_path_free(group_path); + + [contactRows removeValueForKey: group]; + if([contactRows count] == 0) + [contactMap removeObjectForKey: bareJID]; }); } -- (void)rosterWasReceived: (XMPPRoster*)roster_ +- (void)contactManager: (XMPPContactManager*)manager + didAddContact: (XMPPContact*)contact { - of_log(@"Handling roster"); - [[roster_ rosterItems] enumerateKeysAndObjectsUsingBlock: - ^(OFString *bareJID, XMPPRosterItem *item, BOOL *stop) { - OFArray *groups; - OFMapTable *contactRows = [OFMapTable - mapTableWithKeyFunctions: keyFunctions - valueFunctions: rowRefFunctions]; - - [contactMap setObject: contactRows - forKey: bareJID]; - - if (item.groups != nil) - groups = item.groups; - else - groups = @[@"General"]; + XMPPRosterItem *rosterItem = contact.rosterItem; + OFArray *groups = rosterItem.groups;; - for (OFString *group in groups) - [self Jub_addRosterItem: item - group: group]; - }]; + if (groups == nil) + groups = @[ @"General" ]; + + for (OFString *group in groups) + [self Jub_addRosterItem: rosterItem + group: group]; } -- (void)roster: (XMPPRoster*)roster_ - didReceiveRosterItem: (XMPPRosterItem*)item +- (void)contactManager: (XMPPContactManager*)manager + didRemoveContact: (XMPPContact*)contact { - OFArray *groups; - XMPPRosterItem *oldItem = - [roster_.rosterItems objectForKey: [item.JID bareJID]]; + XMPPRosterItem *rosterItem = contact.rosterItem; + OFArray *groups = rosterItem.groups; - if (oldItem) { - if (oldItem.groups != nil) - groups = oldItem.groups; - else - groups = @[@"General"]; + if (groups == nil) + groups = @[ @"General" ]; - for (OFString *group in groups) - [self Jub_removeRosterItem: oldItem - group: group]; + for (OFString *group in groups) + [self Jub_removeRosterItem: rosterItem + group: group]; +} - [contactMap removeObjectForKey: [item.JID bareJID]]; - } +- (void)contact: (XMPPContact*)contact + willUpdateWithRosterItem: (XMPPRosterItem*)rosterItem; +{ + // Remove contact from old set of groups + XMPPRosterItem *oldItem = contact.rosterItem; + OFArray *groups = oldItem.groups; - if (![item.subscription isEqual: @"remove"]) { - OFMapTable *contactRows = [OFMapTable - mapTableWithKeyFunctions: keyFunctions - valueFunctions: rowRefFunctions]; + if (groups == nil) + groups = @[ @"General" ]; - [contactMap setObject: contactRows - forKey: [item.JID bareJID]]; + for (OFString *group in groups) + [self Jub_removeRosterItem: oldItem + group: group]; - if (item.groups != nil) - groups = item.groups; - else - groups = @[@"General"]; + // Add contact to new set of groups + groups = rosterItem.groups; - for (OFString *group in groups) - [self Jub_addRosterItem: item - group: group]; - } + if (groups == nil) + groups = @[ @"General" ]; + + for (OFString *group in groups) + [self Jub_addRosterItem: rosterItem + group: group]; +} + +- (void)contact: (XMPPContact*)contact + didSendPresence: (XMPPPresence*)presence +{ + g_idle_add_block(^{ + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeRowReference *ref; + OFString *bareJID = [contact.rosterItem.JID bareJID]; + OFMapTable *contactRows = [contactMap objectForKey: bareJID]; + + for (OFString *group in contact.rosterItem.groups) { + ref = [contactRows valueForKey: group]; + path = gtk_tree_row_reference_get_path(ref); + gtk_tree_model_get_iter(GTK_TREE_MODEL(roster_model), + &iter, path); + gtk_tree_path_free(path); + + if ([presence.type isEqual: @"available"]) + if (presence.show != nil) + gtk_tree_store_set(roster_model, &iter, + 2, [presence.show UTF8String], -1); + else + gtk_tree_store_set(roster_model, &iter, + 2, "available", -1); + else if ([presence.type isEqual: @"unavailable"]) + gtk_tree_store_set(roster_model, &iter, + 2, "unavailable", -1); + } + + gtk_tree_model_filter_refilter(roster_filter); + }); } +- (void)client: (JubChatClient*)client_ + didChangePresence: (XMPPPresence*)presence +{ + OFString *tooltip = @""; + OFString *show = + [[presence elementForName: @"show" + namespace: XMPP_NS_CLIENT] stringValue]; + OFString *status = + [[presence elementForName: @"status" + namespace: XMPP_NS_CLIENT] stringValue]; + + if (status != nil) + tooltip = [@"Status: " stringByAppendingString: status]; + + g_idle_add_block(^{ + // Block the PresenceComboBox's changed handler, so it doesn't + // fire and resend presence + g_signal_handler_block(presence_combo, + presence_combo_changed_handler_id); + + if ([presence.type isEqual: @"unavailable"]) + gtk_combo_box_set_active_id(presence_combo, + "unavailable"); + else if (show == nil) + gtk_combo_box_set_active_id(presence_combo, + "available"); + else + gtk_combo_box_set_active_id(presence_combo, + [show UTF8String]); + + // Unblock the changed handler + g_signal_handler_unblock(presence_combo, + presence_combo_changed_handler_id); + + gtk_widget_set_tooltip_markup(GTK_WIDGET(presence_combo), + [tooltip UTF8String]); + }); +} @end