X-Git-Url: http://cgit.babelmonkeys.de/?p=jubjub.git;a=blobdiff_plain;f=src%2Fgui%2Fgtk%2FJubGtkRosterUI.m;h=99e51446e75486d6a6f97bafd732c0d9c83c5c8f;hp=ae0d54d778d78ab296d876413f93fe7e5d0f19be;hb=62fb875be3885fcecbed4f614f01975cd200c9c8;hpb=83cc2e59c768a9ec9be5a9e021acb02714c37e8e diff --git a/src/gui/gtk/JubGtkRosterUI.m b/src/gui/gtk/JubGtkRosterUI.m index ae0d54d..99e5144 100644 --- a/src/gui/gtk/JubGtkRosterUI.m +++ b/src/gui/gtk/JubGtkRosterUI.m @@ -9,12 +9,13 @@ static void roster_row_activated(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data) { - JubGtkRosterUI *roster = data; + OFAutoreleasePool *pool; + XMPPContact *contact; + JubChatClient *client = data; GtkTreeIter row_iter; GtkTreeModel *tree_model; gchar *jid_s; - XMPPJID *jid; - OFAutoreleasePool *pool; + OFString *jid; tree_model = gtk_tree_view_get_model(tree_view); gtk_tree_model_get_iter(tree_model, &row_iter, path); @@ -24,10 +25,12 @@ static void roster_row_activated(GtkTreeView *tree_view, GtkTreePath *path, if (!jid_s) return; pool = [OFAutoreleasePool new]; - jid = [XMPPJID JIDWithString: [OFString stringWithUTF8String: jid_s]]; - [roster performSelectorOnMainThread: @selector(chatForJID:) - withObject: jid + jid = [OFString stringWithUTF8String: jid_s]; + contact = [client.contactManager.contacts objectForKey: jid]; + + [client performSelectorOnMainThread: @selector(chatForContact:) + withObject: contact waitUntilDone: NO]; [pool release]; } @@ -45,7 +48,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]; @@ -56,32 +59,21 @@ 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; - - gtk_tree_model_get(model, iter, 1, &jid_s, -1); + char *status; + gtk_tree_model_get(model, iter, 2, &status, -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 -- initWithClient: (JubChatClient*)client; +- initWithClient: (JubChatClient*)client { self = [super init]; @@ -89,47 +81,44 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model, GtkTreeView *roster_view; GtkBuilder *builder; - groupMap = [[OFMapTable alloc] + _groupMap = [[OFMapTable alloc] initWithKeyFunctions: keyFunctions valueFunctions: rowRefFunctions]; - contactMap = [[OFMutableDictionary alloc] init]; - chatMap = [[OFMutableDictionary alloc] init]; - presences = [[OFCountedSet alloc] init]; - connection = [client.connection retain]; + _contactMap = [[OFMutableDictionary alloc] init]; + _client = [client retain]; - [connection addDelegate: self]; - [client.roster addDelegate: self]; + [_client.contactManager addDelegate: self]; builder = gtk_builder_new(); gtk_builder_add_from_file(builder, "data/gtk/roster.ui", NULL); gtk_builder_connect_signals(builder, NULL); - roster_window = + _roster_window = GTK_WIDGET(gtk_builder_get_object(builder, "RosterWindow")); - gtk_widget_show(roster_window); + gtk_widget_show(_roster_window); - roster_model = GTK_TREE_STORE(gtk_builder_get_object(builder, + _roster_model = GTK_TREE_STORE(gtk_builder_get_object(builder, "RosterTreeStore")); - roster_filter = GTK_TREE_MODEL_FILTER( + _roster_filter = GTK_TREE_MODEL_FILTER( gtk_builder_get_object(builder, "RosterTreeModelFilter")); - gtk_tree_model_filter_set_visible_func(roster_filter, - filter_roster_by_presence, presences, NULL); + gtk_tree_model_filter_set_visible_func(_roster_filter, + filter_roster_by_presence, NULL, 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); + G_CALLBACK(roster_row_activated), client); - presence_combo = GTK_COMBO_BOX(gtk_builder_get_object(builder, + _presence_combo = GTK_COMBO_BOX(gtk_builder_get_object(builder, "PresenceComboBox")); - presence_combo_changed_handler_id = - 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) { @@ -142,68 +131,23 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model, - (void)dealloc { - [groupMap release]; - [contactMap release]; - [chatMap release]; - [presences release]; - [connection release]; + [_client.contactManager removeDelegate: self]; + [_groupMap release]; + [_contactMap release]; + [_client release]; - gtk_widget_destroy(roster_window); + 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 +- (void)contact: (XMPPContact*)contact + didSendMessage: (XMPPMessage*)message { - OFAutoreleasePool *pool = [OFAutoreleasePool new]; - JubGtkChatUI *chat = - [chatMap objectForKey: [jid bareJID]]; - if (chat == nil) { - OFString * title = [@"Chat with " stringByAppendingString: - [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: [jid bareJID]]; - } + if (message.body == nil || ![message.type isEqual: @"chat"]) + return; - [pool release]; - - return chat; -} - -- (void)connection: (XMPPConnection*)connection - didReceiveMessage: (XMPPMessage*)message -{ - JubGtkChatUI *chat = [self chatForJID: message.from]; + id chat = [_client chatForContact: contact]; [chat addMessage: message.body sender: [message.from bareJID]]; } @@ -216,50 +160,67 @@ 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; - group_ref = [groupMap valueForKey: group]; + if (!(contactRows = [_contactMap objectForKey: bareJID])) { + contactRows = [OFMapTable + mapTableWithKeyFunctions: keyFunctions + valueFunctions: rowRefFunctions]; + + [_contactMap setObject: contactRows + forKey: 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, + 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); + _roster_model), &group_iter); group_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL( - roster_model), group_path); + _roster_model), group_path); - [groupMap setValue: group_ref - forKey: group]; + [_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), + 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); + gtk_tree_store_append(_roster_model, &contact_iter, + &group_iter); if (item.name) - gtk_tree_store_set(roster_model, &contact_iter, + 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, + 1, [bareJID UTF8String], + 2, "unavailable", -1); + else if (item.JID.node) + 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); + else + gtk_tree_store_set(_roster_model, &contact_iter, + 0, [item.JID.domain UTF8String], + 1, [bareJID UTF8String], + 2, "unavailable", -1); contact_path = gtk_tree_model_get_path(GTK_TREE_MODEL( - roster_model), &contact_iter); + _roster_model), &contact_iter); contact_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL( - roster_model), contact_path); + _roster_model), contact_path); gtk_tree_path_free(contact_path); @@ -275,95 +236,149 @@ 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), + 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); + gtk_tree_store_remove(_roster_model, &contact_iter); - group_ref = [groupMap valueForKey: group]; + 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), + 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]; + 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); + + [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 +{ + OFDictionary *allPresences = [contact presences]; + XMPPPresence *highPresence = [[[allPresences allObjects] sortedArray] + lastObject]; + OFMutableString *tooltip = + [OFMutableString stringWithString: @"Resources:"]; + + [allPresences enumerateKeysAndObjectsUsingBlock: + ^(OFString *resource, XMPPPresence *pres, BOOL *stop) { + [tooltip appendString: @"\n"]; + [tooltip appendString: resource]; + if ([pres.type isEqual: @"available"]) { + if (pres.show != nil) + [tooltip appendFormat: @" (%@)", pres.show]; + else + [tooltip appendString: @" (available)"]; + } else + [tooltip appendString: @" (unavailable)"]; + + if (pres.status) + [tooltip appendFormat: @": %@", pres.status]; + }]; + + 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 ([highPresence.type isEqual: @"available"]) { + if (highPresence.show != nil) + gtk_tree_store_set(_roster_model, &iter, + 2, [highPresence.show UTF8String], + -1); + else + gtk_tree_store_set(_roster_model, &iter, + 2, "available", -1); + } else + gtk_tree_store_set(_roster_model, &iter, + 2, "unavailable", -1); + + gtk_tree_store_set(_roster_model, &iter, + 3, [tooltip UTF8String], -1); + } + + gtk_tree_model_filter_refilter(_roster_filter); + }); } -- (void)client: (JubChatClient*)client +- (void)client: (JubChatClient*)client_ didChangePresence: (XMPPPresence*)presence { OFString *tooltip = @""; @@ -380,24 +395,24 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model, 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); + g_signal_handler_block(_presence_combo, + _presence_combo_changed_handler_id); if ([presence.type isEqual: @"unavailable"]) - gtk_combo_box_set_active_id(presence_combo, + gtk_combo_box_set_active_id(_presence_combo, "unavailable"); else if (show == nil) - gtk_combo_box_set_active_id(presence_combo, + gtk_combo_box_set_active_id(_presence_combo, "available"); else - gtk_combo_box_set_active_id(presence_combo, + 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); + g_signal_handler_unblock(_presence_combo, + _presence_combo_changed_handler_id); - gtk_widget_set_tooltip_markup(GTK_WIDGET(presence_combo), + gtk_widget_set_tooltip_markup(GTK_WIDGET(_presence_combo), [tooltip UTF8String]); }); }