<column type="gchararray"/>
</columns>
</object>
+ <object class="GtkTreeModelFilter" id="RosterTreeModelFilter">
+ <property name="child_model">RosterTreeStore</property>
+ </object>
<object class="GtkWindow" id="RosterWindow">
<property name="can_focus">False</property>
<property name="title" translatable="yes">JubJub</property>
<object class="GtkTreeView" id="RosterTreeView">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="model">RosterTreeStore</property>
+ <property name="model">RosterTreeModelFilter</property>
<property name="search_column">0</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="RosterTreeViewSelection"/>
#import "JubGtkUI.h"
@interface AppDelegate: OFObject
- <OFApplicationDelegate, XMPPConnectionDelegate>
+ <OFApplicationDelegate, XMPPConnectionDelegate, XMPPRosterDelegate>
{
XMPPConnection *connection;
XMPPRoster *roster;
[connection connect];
[connection handleConnection];
+ [connection addDelegate: [ui rosterDelegate]];
+
roster = [[XMPPRoster alloc] initWithConnection: connection];
[roster addDelegate: [ui rosterDelegate]];
+ [roster addDelegate: self];
[ui startUIThread];
}
[roster requestRoster];
}
+- (void)rosterWasReceived: (XMPPRoster*)roster
+{
+ [connection sendStanza: [XMPPPresence presence]];
+}
+
- (void)connection: (XMPPConnection*)conn
didReceiveElement: (OFXMLElement*)element
{
gtk_main_quit();
}
+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);
+
+ // Groups have no JID
+ if (!jid_s)
+ return TRUE;
+
+ jid = [[OFString alloc] initWithUTF8String: jid_s];
+
+ g_free(jid_s);
+
+ if ([presences countForObject: jid]) {
+ [jid release];
+ return TRUE;
+ } else {
+ [jid release];
+ return FALSE;
+ }
+}
+
@implementation JubGtkUI
- init
{
initWithKeyFunctions: keyFunctions
valueFunctions: rowRefFunctions];
contactMap = [[OFMutableDictionary alloc] init];
+ presences = [[OFCountedSet alloc] init];
} @catch (id e) {
[self release];
@throw e;
[groupMap release];
[contactMap release];
+ [presences release];
if (roster_model)
g_object_unref(roster_model);
roster_model =
GTK_TREE_STORE(gtk_builder_get_object(builder, "RosterTreeStore"));
+ 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_builder_connect_signals(builder, NULL);
g_object_unref(G_OBJECT(builder));
return self;
}
+/* Presence handling */
+static gboolean refilter_roster(gpointer data)
+{
+ gtk_tree_model_filter_refilter(data);
+
+ return FALSE;
+}
+
+- (void)connection: (XMPPConnection*)connection
+ didReceivePresence: (XMPPPresence*)presence
+{
+ if ([presence.type isEqual: @"available"])
+ [presences addObject: [presence.from bareJID]];
+ else
+ [presences removeObject: [presence.from bareJID]];
+
+ g_idle_add(refilter_roster, roster_filter);
+}
+
/* Roster Delegate methods */
struct add_roster_item_param {
OFString *group;
return FALSE;
}
-- (void)rosterWasReceived: (XMPPRoster*)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"];
-
- 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);
- }
- }];
-}
-
struct remove_roster_item_param {
OFString *group;
OFMapTable *groupMap;
return FALSE;
}
+- (void)rosterWasReceived: (XMPPRoster*)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"];
+
+ 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);
+ }
+ }];
+}
+
- (void)roster: (XMPPRoster*)roster_
didReceiveRosterItem: (XMPPRosterItem*)item
{