]> cgit.babelmonkeys.de Git - jubjub.git/blobdiff - src/gui/gtk/JubGtkRosterUI.m
Move away from BOOL
[jubjub.git] / src / gui / gtk / JubGtkRosterUI.m
index ac74a33aae1e44f23041bb4cf49886a4112a9691..432677cb8f1e2c9e92cbebf6efb29f2329931188 100644 (file)
@@ -1,5 +1,6 @@
 #import <ObjXMPP/namespaces.h>
 #include <string.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
 
 #import "JubGtkRosterUI.h"
 #import "JubGObjectMap.h"
@@ -31,27 +32,17 @@ static void roster_row_activated(GtkTreeView *tree_view, GtkTreePath *path,
 
        [client performSelectorOnMainThread: @selector(chatForContact:)
                                 withObject: contact
-                             waitUntilDone: NO];
+                             waitUntilDone: false];
        [pool release];
 }
 
 static void presence_changed(GtkComboBox *combo_box, gpointer data)
 {
-       XMPPPresence *pres;
-       XMPPConnection *connection = data;
+       JubChatClient *client = 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 setShow: @(status)];
-       }
-
-       [connection sendStanza: pres];
+       [client sendPresenceWithStatus: @(status)];
 
        [pool release];
 }
@@ -59,6 +50,7 @@ static void presence_changed(GtkComboBox *combo_box, gpointer data)
 static gboolean filter_roster_by_presence(GtkTreeModel *model,
     GtkTreeIter *iter, gpointer data)
 {
+       bool *showOffline = data;
        char *status;
        gtk_tree_model_get(model, iter, 2, &status, -1);
 
@@ -66,28 +58,47 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
        if (!status)
                return TRUE;
 
-       if (!strcmp(status, "unavailable"))
+       if (!*showOffline && !strcmp(status, "unavailable"))
                return FALSE;
 
        return TRUE;
 }
 
+static void menu_show_offline(GtkCheckMenuItem *checkmenuitem, gpointer data)
+{
+       JubGtkRosterUI *ui = data;
+       ui.showOffline = gtk_check_menu_item_get_active(checkmenuitem);
+}
+
+static void dialog_response_callback(GtkDialog *dialog, gint response_id,
+    gpointer user_data)
+{
+       void (^block)(gint) = user_data;
+       block(response_id);
+       [block release];
+}
+
 @implementation JubGtkRosterUI
 - initWithClient: (JubChatClient*)client
 {
        self = [super init];
 
        @try {
+               GtkCheckMenuItem *show_offline_menu_item;
                GtkTreeView *roster_view;
                GtkBuilder *builder;
 
                _groupMap = [[OFMapTable alloc]
                    initWithKeyFunctions: keyFunctions
                          valueFunctions: rowRefFunctions];
+               _subDialogMap = [[OFMapTable alloc]
+                   initWithKeyFunctions: keyFunctions
+                   valueFunctions: gObjectFunctions];
                _contactMap = [[OFMutableDictionary alloc] init];
                _client = [client retain];
 
                [_client.contactManager addDelegate: self];
+               [_client.avatarManager setDelegate: self];
 
                builder = gtk_builder_new();
                gtk_builder_add_from_file(builder, "data/gtk/roster.ui", NULL);
@@ -105,7 +116,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, NULL, NULL);
+                   filter_roster_by_presence, &_showOffline, NULL);
 
                roster_view = GTK_TREE_VIEW(gtk_builder_get_object(builder,
                        "RosterTreeView"));
@@ -118,7 +129,14 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
 
                _presence_combo_changed_handler_id =
                    g_signal_connect(_presence_combo, "changed",
-                       G_CALLBACK(presence_changed), client.connection);
+                       G_CALLBACK(presence_changed), client);
+
+               show_offline_menu_item =
+                   GTK_CHECK_MENU_ITEM(gtk_builder_get_object(builder,
+                       "showOfflineCheckMenuItem"));
+
+               g_signal_connect(show_offline_menu_item, "toggled",
+                   G_CALLBACK(menu_show_offline), self);
 
                g_object_unref(G_OBJECT(builder));
        } @catch (id e) {
@@ -131,7 +149,9 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
 
 - (void)dealloc
 {
+       [_client.avatarManager setDelegate: nil];
        [_client.contactManager removeDelegate: self];
+       [_subDialogMap release];
        [_groupMap release];
        [_contactMap release];
        [_client release];
@@ -141,6 +161,19 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
        [super dealloc];
 }
 
+- (void)Jub_closeSubscribeDialogForRosterItem: (XMPPRosterItem*)rosterItem
+{
+       // Close subscripton dialogs, answered on another client
+       GtkDialog *dialog;
+       OFString *subscription = rosterItem.subscription;
+       OFString *bareJID = [rosterItem.JID bareJID];
+
+       if (([subscription isEqual: @"from"] ||
+            [subscription isEqual: @"both"]) &&
+           (dialog = [_subDialogMap valueForKey: bareJID]) != NULL)
+               gtk_dialog_response(dialog, GTK_RESPONSE_NONE);
+}
+
 /* Roster Delegate methods */
 - (void)Jub_addRosterItem: (XMPPRosterItem*)item
                    group: (OFString*)group
@@ -264,6 +297,8 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
                    Jub_addRosterItem:group:)
                                       withObject: rosterItem
                                       withObject: group];
+
+       [self Jub_closeSubscribeDialogForRosterItem: rosterItem];
 }
 
 - (void)contactManager: (XMPPContactManager*)manager
@@ -282,9 +317,52 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
                                       withObject: group];
 }
 
+-          (void)contactManager: (XMPPContactManager*)manager
+  didReceiveSubscriptionRequest: (XMPPPresence*)presence
+{
+       XMPPJID *JID = presence.from;
+       OFString *bareJID = [JID bareJID];
+       OFString *message = [OFString stringWithFormat: @"<b>%@</b> would like "
+           @"to subscribe to your presence.", bareJID];
+       g_idle_add_block(^{
+               GtkWidget *dialog, *content_area, *label;
+
+               if ([_subDialogMap valueForKey: JID] != NULL)
+                       return;
+
+               dialog = gtk_dialog_new_with_buttons("Subscription Request",
+                   GTK_WINDOW(_roster_window), GTK_DIALOG_DESTROY_WITH_PARENT,
+                   "Accept", GTK_RESPONSE_ACCEPT,
+                   "Deny", GTK_RESPONSE_REJECT, NULL);
+
+               content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+               label = gtk_label_new(NULL);
+               gtk_label_set_markup(GTK_LABEL(label), [message UTF8String]);
+               gtk_container_add(GTK_CONTAINER(content_area), label);
+
+               g_signal_connect(dialog, "response",
+                   G_CALLBACK(dialog_response_callback),
+                   [^(gint response_id) {
+                       if (response_id == GTK_RESPONSE_ACCEPT)
+                               [manager sendSubscribedToJID: JID];
+                       else if (response_id == GTK_RESPONSE_REJECT)
+                               [manager sendUnsubscribedToJID: JID];
+                       [_subDialogMap removeValueForKey: bareJID];
+                       gtk_widget_destroy(GTK_WIDGET(dialog));
+               } copy]);
+
+               [_subDialogMap setValue: dialog
+                                forKey: bareJID];
+
+               gtk_widget_show_all(dialog);
+       });
+}
+
 -           (void)contact: (XMPPContact*)contact
   willUpdateWithRosterItem: (XMPPRosterItem*)rosterItem;
 {
+       [self Jub_closeSubscribeDialogForRosterItem: rosterItem];
+
        // Remove contact from old set of groups
        XMPPRosterItem *oldItem = contact.rosterItem;
        OFArray *groups = oldItem.groups;
@@ -321,7 +399,7 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
            [OFMutableString stringWithString: @"<b>Resources:</b>"];
 
        [allPresences enumerateKeysAndObjectsUsingBlock:
-           ^(OFString *resource, XMPPPresence *pres, BOOL *stop) {
+           ^(OFString *resource, XMPPPresence *pres, bool *stop) {
                [tooltip appendString: @"\n"];
                [tooltip appendString: resource];
                if ([pres.type isEqual: @"available"]) {
@@ -374,6 +452,39 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
        });
 }
 
+- (void)contact: (XMPPContact*)contact
+   didSetAvatar: (OFString*)avatarFile
+{
+       of_log(@"Got an avatar from %@", contact.rosterItem.JID);
+       g_idle_add_block(^{
+               GtkTreeIter iter;
+               GtkTreePath *path;
+               GtkTreeRowReference *ref;
+               OFString *bareJID = [contact.rosterItem.JID bareJID];
+               OFMapTable *contactRows = [_contactMap objectForKey: bareJID];
+               OFArray *groups = contact.rosterItem.groups;;
+
+               GdkPixbuf *avatar =
+                   gdk_pixbuf_new_from_file_at_size([avatarFile UTF8String],
+                       32, 32, NULL);
+
+               if (groups == nil)
+                       groups = @[ @"General" ];
+
+               for (OFString *group in 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);
+
+                       gtk_tree_store_set(_roster_model, &iter,
+                           4, avatar, -1);
+               }
+               g_object_unref(G_OBJECT(avatar));
+       });
+}
+
 -      (void)client: (JubChatClient*)client_
   didChangePresence: (XMPPPresence*)presence
 {
@@ -412,4 +523,17 @@ static gboolean filter_roster_by_presence(GtkTreeModel *model,
                    [tooltip UTF8String]);
        });
 }
+
+- (bool)showOffline
+{
+       OF_GETTER(_showOffline, true);
+}
+
+- (void)setShowOffline: (bool)showOffline
+{
+       _showOffline = showOffline;
+       g_idle_add_block(^{
+               gtk_tree_model_filter_refilter(_roster_filter);
+       });
+}
 @end