]> git.0d.be Git - empathy.git/commitdiff
Merge branch 'create-cancel-crash'
authorFelix Kaser <f.kaser@gmx.net>
Mon, 14 Dec 2009 07:18:19 +0000 (08:18 +0100)
committerFelix Kaser <f.kaser@gmx.net>
Mon, 14 Dec 2009 07:18:19 +0000 (08:18 +0100)
libempathy-gtk/empathy-account-chooser.c
libempathy-gtk/empathy-new-message-dialog.c
libempathy-gtk/empathy-new-message-dialog.h
libempathy-gtk/empathy-new-message-dialog.ui
libempathy-gtk/empathy-spell.c
libempathy/empathy-tp-contact-list.c

index 61d63b32c35026296b454de2208eaa07f79454fb..bd46efc16a0a326902caf910443658733f7d5e3d 100644 (file)
@@ -503,6 +503,41 @@ account_manager_prepared_cb (GObject *source_object,
        g_signal_emit (chooser, signals[READY], 0);
 }
 
+static gint
+account_cmp (GtkTreeModel *model,
+            GtkTreeIter *a,
+            GtkTreeIter *b,
+            gpointer user_data)
+{
+       gboolean a_enabled, b_enabled;
+       gchar *a_text, *b_text;
+       gint result;
+
+       gtk_tree_model_get (model, a, COL_ACCOUNT_ENABLED, &a_enabled, -1);
+       gtk_tree_model_get (model, b, COL_ACCOUNT_ENABLED, &b_enabled, -1);
+
+       /* Enabled accounts are displayed first */
+       if (a_enabled != b_enabled)
+               return a_enabled ? -1: 1;
+
+       gtk_tree_model_get (model, a, COL_ACCOUNT_TEXT, &a_text, -1);
+       gtk_tree_model_get (model, b, COL_ACCOUNT_TEXT, &b_text, -1);
+
+       if (a_text == b_text)
+               result = 0;
+       else if (a_text == NULL)
+               result = 1;
+       else if (b_text == NULL)
+               result = -1;
+       else
+               result = g_ascii_strcasecmp (a_text, b_text);
+
+       g_free (a_text);
+       g_free (b_text);
+
+       return result;
+}
+
 static void
 account_chooser_setup (EmpathyAccountChooser *chooser)
 {
@@ -524,6 +559,11 @@ account_chooser_setup (EmpathyAccountChooser *chooser)
                                    G_TYPE_BOOLEAN,   /* Enabled */
                                    TP_TYPE_ACCOUNT);
 
+       gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (store),
+               account_cmp, chooser, NULL);
+       gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
+               GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
+
        gtk_combo_box_set_model (combobox, GTK_TREE_MODEL (store));
 
        renderer = gtk_cell_renderer_pixbuf_new ();
index c1069f87950e53667f055995fdecb84b95e4a999..c7055017e4019ea3903ea9b55d835c69eb0bc9a3 100644 (file)
 #include <libempathy/empathy-debug.h>
 
 #include <libempathy-gtk/empathy-ui-utils.h>
+#include <libempathy-gtk/empathy-images.h>
 
 #include "empathy-new-message-dialog.h"
 #include "empathy-account-chooser.h"
 
+static EmpathyNewMessageDialog *dialog_singleton = NULL;
+
+G_DEFINE_TYPE(EmpathyNewMessageDialog, empathy_new_message_dialog,
+                                      GTK_TYPE_DIALOG)
+
 /**
  * SECTION:empathy-new-message-dialog
  * @title: EmpathyNewMessageDialog
@@ -51,7 +57,9 @@
  * call to be started with any contact on any enabled account.
  */
 
-typedef struct {
+typedef struct _EmpathyNewMessageDialogPriv EmpathyNewMessageDialogPriv;
+
+struct _EmpathyNewMessageDialogPriv {
        GtkWidget *dialog;
        GtkWidget *table_contact;
        GtkWidget *account_chooser;
@@ -59,7 +67,11 @@ typedef struct {
        GtkWidget *button_chat;
        GtkWidget *button_call;
        EmpathyContactManager *contact_manager;
-} EmpathyNewMessageDialog;
+};
+
+#define GET_PRIV(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_NEW_MESSAGE_DIALOG, \
+    EmpathyNewMessageDialogPriv))
 
 enum {
        COMPLETION_COL_TEXT,
@@ -71,6 +83,7 @@ static void
 new_message_dialog_account_changed_cb (GtkWidget               *widget,
                                       EmpathyNewMessageDialog *dialog)
 {
+       EmpathyNewMessageDialogPriv *priv = GET_PRIV (dialog);
        EmpathyAccountChooser *chooser;
        TpConnection          *connection;
        EmpathyTpContactList *contact_list;
@@ -81,17 +94,17 @@ new_message_dialog_account_changed_cb (GtkWidget               *widget,
        gchar                *tmpstr;
 
        /* Remove completions */
-       completion = gtk_entry_get_completion (GTK_ENTRY (dialog->entry_id));
+       completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry_id));
        store = GTK_LIST_STORE (gtk_entry_completion_get_model (completion));
        gtk_list_store_clear (store);
 
        /* Get members of the new account */
-       chooser = EMPATHY_ACCOUNT_CHOOSER (dialog->account_chooser);
+       chooser = EMPATHY_ACCOUNT_CHOOSER (priv->account_chooser);
        connection = empathy_account_chooser_get_connection (chooser);
        if (!connection) {
                return;
        }
-       contact_list = empathy_contact_manager_get_list (dialog->contact_manager,
+       contact_list = empathy_contact_manager_get_list (priv->contact_manager,
                                                         connection);
        members = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (contact_list));
 
@@ -126,6 +139,7 @@ new_message_dialog_match_selected_cb (GtkEntryCompletion *widget,
                                      GtkTreeIter        *iter,
                                      EmpathyNewMessageDialog *dialog)
 {
+       EmpathyNewMessageDialogPriv *priv = GET_PRIV (dialog);
        gchar *id;
 
        if (!iter || !model) {
@@ -133,7 +147,7 @@ new_message_dialog_match_selected_cb (GtkEntryCompletion *widget,
        }
 
        gtk_tree_model_get (model, iter, COMPLETION_COL_ID, &id, -1);
-       gtk_entry_set_text (GTK_ENTRY (dialog->entry_id), id);
+       gtk_entry_set_text (GTK_ENTRY (priv->entry_id), id);
 
        DEBUG ("Got selected match **%s**", id);
 
@@ -199,12 +213,13 @@ new_message_dialog_response_cb (GtkWidget               *widget,
                                gint                    response,
                                EmpathyNewMessageDialog *dialog)
 {
+       EmpathyNewMessageDialogPriv *priv = GET_PRIV (dialog);
        TpConnection *connection;
        const gchar *id;
 
        connection = empathy_account_chooser_get_connection (
-               EMPATHY_ACCOUNT_CHOOSER (dialog->account_chooser));
-       id = gtk_entry_get_text (GTK_ENTRY (dialog->entry_id));
+               EMPATHY_ACCOUNT_CHOOSER (priv->account_chooser));
+       id = gtk_entry_get_text (GTK_ENTRY (priv->entry_id));
        if (!connection || EMP_STR_EMPTY (id)) {
                gtk_widget_destroy (widget);
                return;
@@ -229,62 +244,94 @@ static void
 new_message_change_state_button_cb  (GtkEditable             *editable,
                                     EmpathyNewMessageDialog *dialog)
 {
+       EmpathyNewMessageDialogPriv *priv = GET_PRIV (dialog);
        const gchar *id;
        gboolean     sensitive;
 
        id = gtk_entry_get_text (GTK_ENTRY (editable));
        sensitive = !EMP_STR_EMPTY (id);
 
-       gtk_widget_set_sensitive (dialog->button_chat, sensitive);
-       gtk_widget_set_sensitive (dialog->button_call, sensitive);
+       gtk_widget_set_sensitive (priv->button_chat, sensitive);
+       gtk_widget_set_sensitive (priv->button_call, sensitive);
 }
 
-static void
-new_message_dialog_destroy_cb (GtkWidget               *widget,
-                              EmpathyNewMessageDialog *dialog)
+static GObject *
+empathy_new_message_dialog_constructor (GType type,
+                                       guint n_props,
+                                       GObjectConstructParam *props)
 {
-       g_object_unref (dialog->contact_manager);
-       g_free (dialog);
+       GObject *retval;
+
+       if (dialog_singleton) {
+               retval = G_OBJECT (dialog_singleton);
+               g_object_ref (retval);
+       }
+       else {
+               retval = G_OBJECT_CLASS (
+               empathy_new_message_dialog_parent_class)->constructor (type,
+                       n_props, props);
+
+               dialog_singleton = EMPATHY_NEW_MESSAGE_DIALOG (retval);
+               g_object_add_weak_pointer (retval, (gpointer) &dialog_singleton);
+       }
+
+       return retval;
 }
 
-/**
- * empathy_new_message_dialog_show:
- * @parent: parent #GtkWindow of the dialog
- *
- * Create a new #EmpathyNewMessageDialog and show it.
- *
- * Return value: the new #EmpathyNewMessageDialog
- */
-GtkWidget *
-empathy_new_message_dialog_show (GtkWindow *parent)
+static void
+empathy_new_message_dialog_init (EmpathyNewMessageDialog *dialog)
 {
-       static EmpathyNewMessageDialog *dialog = NULL;
+       EmpathyNewMessageDialogPriv *priv = GET_PRIV (dialog);
        GtkBuilder                     *gui;
        gchar                          *filename;
        GtkEntryCompletion             *completion;
        GtkListStore                   *model;
-
-       if (dialog) {
-               gtk_window_present (GTK_WINDOW (dialog->dialog));
-               return dialog->dialog;
-       }
-
-       dialog = g_new0 (EmpathyNewMessageDialog, 1);
+       GtkWidget                      *content_area;
+       GtkWidget                      *image;
 
        /* create a contact manager */
-       dialog->contact_manager = empathy_contact_manager_dup_singleton ();
+       priv->contact_manager = empathy_contact_manager_dup_singleton ();
 
        filename = empathy_file_lookup ("empathy-new-message-dialog.ui",
                                        "libempathy-gtk");
        gui = empathy_builder_get_file (filename,
-                                       "new_message_dialog", &dialog->dialog,
-                                       "table_contact", &dialog->table_contact,
-                                       "entry_id", &dialog->entry_id,
-                                       "button_chat", &dialog->button_chat,
-                                       "button_call",&dialog->button_call,
+                                       "table_contact", &priv->table_contact,
+                                       "entry_id", &priv->entry_id,
                                        NULL);
        g_free (filename);
 
+       content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+       gtk_container_add (GTK_CONTAINER (content_area), priv->table_contact);
+
+       /* add buttons */
+       gtk_dialog_add_button (GTK_DIALOG (dialog),
+               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+
+       priv->button_call = gtk_button_new_with_mnemonic (_("C_all"));
+       image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_VOIP,
+               GTK_ICON_SIZE_BUTTON);
+       gtk_button_set_image (GTK_BUTTON (priv->button_call), image);
+
+       gtk_dialog_add_action_widget (GTK_DIALOG (dialog), priv->button_call, 1);
+       gtk_widget_show (priv->button_call);
+
+       priv->button_chat = gtk_button_new_with_mnemonic (_("C_hat"));
+       image = gtk_image_new_from_icon_name (EMPATHY_IMAGE_NEW_MESSAGE,
+               GTK_ICON_SIZE_BUTTON);
+       gtk_button_set_image (GTK_BUTTON (priv->button_chat), image);
+
+       gtk_dialog_add_action_widget (GTK_DIALOG (dialog), priv->button_chat, 2);
+       gtk_widget_show (priv->button_chat);
+
+       /* Tweak the dialog */
+       gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+
+       gtk_window_set_title (GTK_WINDOW (dialog), _("New Conversation"));
+       gtk_window_set_role (GTK_WINDOW (dialog), "new_message");
+       gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+       gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
+       gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
+
        /* text completion */
        completion = gtk_entry_completion_new ();
        model = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
@@ -293,47 +340,88 @@ empathy_new_message_dialog_show (GtkWindow *parent)
                                             new_message_dialog_match_func,
                                             NULL, NULL);
        gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (model));
-       gtk_entry_set_completion (GTK_ENTRY (dialog->entry_id), completion);
+       gtk_entry_set_completion (GTK_ENTRY (priv->entry_id), completion);
        g_signal_connect (completion, "match-selected",
                          G_CALLBACK (new_message_dialog_match_selected_cb),
                          dialog);
        g_object_unref (completion);
        g_object_unref (model);
 
+       g_signal_connect (dialog, "response",
+                   G_CALLBACK (new_message_dialog_response_cb), dialog);
+
        empathy_builder_connect (gui, dialog,
-                              "new_message_dialog", "destroy", new_message_dialog_destroy_cb,
-                              "new_message_dialog", "response", new_message_dialog_response_cb,
                               "entry_id", "changed", new_message_change_state_button_cb,
                               NULL);
 
-       g_object_add_weak_pointer (G_OBJECT (dialog->dialog), (gpointer) &dialog);
-
        g_object_unref (gui);
 
        /* Create account chooser */
-       dialog->account_chooser = empathy_account_chooser_new ();
-       gtk_table_attach_defaults (GTK_TABLE (dialog->table_contact),
-                                  dialog->account_chooser,
+       priv->account_chooser = empathy_account_chooser_new ();
+       gtk_table_attach_defaults (GTK_TABLE (priv->table_contact),
+                                  priv->account_chooser,
                                   1, 2, 0, 1);
-       empathy_account_chooser_set_filter (EMPATHY_ACCOUNT_CHOOSER (dialog->account_chooser),
+       empathy_account_chooser_set_filter (EMPATHY_ACCOUNT_CHOOSER (priv->account_chooser),
                                            empathy_account_chooser_filter_is_connected,
                                            NULL);
-       gtk_widget_show (dialog->account_chooser);
+       gtk_widget_show (priv->account_chooser);
 
-       new_message_dialog_account_changed_cb (dialog->account_chooser, dialog);
-       g_signal_connect (dialog->account_chooser, "changed",
+       new_message_dialog_account_changed_cb (priv->account_chooser, dialog);
+       g_signal_connect (priv->account_chooser, "changed",
                          G_CALLBACK (new_message_dialog_account_changed_cb),
                          dialog);
 
-       if (parent) {
-               gtk_window_set_transient_for (GTK_WINDOW (dialog->dialog),
-                                             GTK_WINDOW (parent));
+       gtk_widget_set_sensitive (priv->button_chat, FALSE);
+       gtk_widget_set_sensitive (priv->button_call, FALSE);
+}
+
+static void
+empathy_new_message_dialog_dispose (GObject *object)
+{
+       EmpathyNewMessageDialogPriv *priv = GET_PRIV (object);
+
+       if (priv->contact_manager != NULL) {
+               g_object_unref (priv->contact_manager);
+               priv->contact_manager = NULL;
        }
 
-       gtk_widget_set_sensitive (dialog->button_chat, FALSE);
-       gtk_widget_set_sensitive (dialog->button_call, FALSE);
+       if (G_OBJECT_CLASS (empathy_new_message_dialog_parent_class)->dispose)
+               G_OBJECT_CLASS (empathy_new_message_dialog_parent_class)->dispose (object);
+}
+
+static void
+empathy_new_message_dialog_class_init (
+  EmpathyNewMessageDialogClass *class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+       g_type_class_add_private (class, sizeof (EmpathyNewMessageDialogPriv));
+
+       object_class->constructor = empathy_new_message_dialog_constructor;
+
+       object_class->dispose = empathy_new_message_dialog_dispose;
+}
 
-       gtk_widget_show (dialog->dialog);
+/**
+ * empathy_new_message_dialog_new:
+ * @parent: parent #GtkWindow of the dialog
+ *
+ * Create a new #EmpathyNewMessageDialog it.
+ *
+ * Return value: the new #EmpathyNewMessageDialog
+ */
+GtkWidget *
+empathy_new_message_dialog_show (GtkWindow *parent)
+{
+       GtkWidget *dialog;
+
+       dialog = g_object_new (EMPATHY_TYPE_NEW_MESSAGE_DIALOG, NULL);
+
+       if (parent) {
+               gtk_window_set_transient_for (GTK_WINDOW (dialog),
+                                             GTK_WINDOW (parent));
+       }
 
-       return dialog->dialog;
+       gtk_widget_show (dialog);
+       return dialog;
 }
index 08887c5e8c8f093ceb90f452349f5fdec6c41217..6e01619dbd5d33094491b9e290e2318850ecfcfd 100644 (file)
 #ifndef __EMPATHY_NEW_MESSAGE_DIALOG_H__
 #define __EMPATHY_NEW_MESSAGE_DIALOG_H__
 
+#include <glib-object.h>
 #include <gtk/gtk.h>
 
 G_BEGIN_DECLS
 
-GtkWidget *empathy_new_message_dialog_show (GtkWindow *parent);
+typedef struct _EmpathyNewMessageDialog EmpathyNewMessageDialog;
+typedef struct _EmpathyNewMessageDialogClass EmpathyNewMessageDialogClass;
+
+struct _EmpathyNewMessageDialogClass {
+    GtkDialogClass parent_class;
+};
+
+struct _EmpathyNewMessageDialog {
+    GtkDialog parent;
+};
+
+GType empathy_new_message_dialog_get_type (void);
+
+/* TYPE MACROS */
+#define EMPATHY_TYPE_NEW_MESSAGE_DIALOG \
+  (empathy_new_message_dialog_get_type ())
+#define EMPATHY_NEW_MESSAGE_DIALOG(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj), EMPATHY_TYPE_NEW_MESSAGE_DIALOG, \
+    EmpathyNewMessageDialog))
+#define EMPATHY_NEW_MESSAGE_DIALOG_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_NEW_MESSAGE_DIALOG, \
+    EmpathyNewMessageDialogClass))
+#define EMPATHY_IS_NEW_MESSAGE_DIALOG(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMPATHY_TYPE_NEW_MESSAGE_DIALOG))
+#define EMPATHY_IS_NEW_MESSAGE_DIALOG_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), EMPATHY_TYPE_NEW_MESSAGE_DIALOG))
+#define EMPATHY_NEW_MESSAGE_DIALOG_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_NEW_MESSAGE_DIALOG, \
+    EmpathyNewMessageDialogClass))
+
+GtkWidget * empathy_new_message_dialog_show (GtkWindow *parent);
 
 G_END_DECLS
 
index a7b43818c2f643b9b4106d738da37fcbe8a7682f..009028ec1d58b7516818169c6c831ded13225710 100644 (file)
 <?xml version="1.0"?>
-<!--*- mode: xml -*-->
 <interface>
-  <object class="GtkImage" id="call_image">
-    <property name="icon_name">audio-input-microphone</property>
-    <property name="icon-size">4</property>
-  </object>
-  <object class="GtkImage" id="chat_image">
-    <property name="icon_name">im-message-new</property>
-    <property name="icon-size">4</property>
-  </object>
-  <object class="GtkDialog" id="new_message_dialog">
-    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-    <property name="border_width">5</property>
-    <property name="title" translatable="yes">New Conversation</property>
-    <property name="resizable">False</property>
-    <property name="role">new_message</property>
-    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
-    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
-    <property name="has_separator">False</property>
-    <child internal-child="vbox">
-      <object class="GtkVBox" id="dialog-vbox1">
+  <!-- interface-requires gtk+ 2.12 -->
+  <!-- interface-naming-policy toplevel-contextual -->
+  <object class="GtkTable" id="table_contact">
+    <property name="visible">True</property>
+    <property name="n_rows">2</property>
+    <property name="n_columns">2</property>
+    <property name="column_spacing">6</property>
+    <property name="row_spacing">6</property>
+    <child>
+      <object class="GtkLabel" id="label1">
+        <property name="visible">True</property>
+        <property name="xalign">0</property>
+        <property name="label" translatable="yes">Account:</property>
+      </object>
+      <packing>
+        <property name="x_options"></property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="label2">
         <property name="visible">True</property>
-        <property name="spacing">2</property>
-        <child>
-          <object class="GtkTable" id="table_contact">
-            <property name="visible">True</property>
-            <property name="n_rows">2</property>
-            <property name="n_columns">2</property>
-            <property name="column_spacing">6</property>
-            <property name="row_spacing">6</property>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <object class="GtkLabel" id="label1">
-                <property name="visible">True</property>
-                <property name="xalign">0</property>
-                <property name="label" translatable="yes">Account:</property>
-              </object>
-              <packing>
-                <property name="x_options"/>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkLabel" id="label2">
-                <property name="visible">True</property>
-                <property name="xalign">0</property>
-                <property name="label" translatable="yes">Contact ID:</property>
-              </object>
-              <packing>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
-                <property name="x_options"/>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkEntry" id="entry_id">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="activates_default">True</property>
-              </object>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="position">1</property>
-          </packing>
-        </child>
-        <child internal-child="action_area">
-          <object class="GtkHButtonBox" id="dialog-action_area1">
-            <property name="visible">True</property>
-            <property name="layout_style">GTK_BUTTONBOX_END</property>
-            <child>
-              <object class="GtkButton" id="button1">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="label">gtk-cancel</property>
-                <property name="use_stock">True</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkButton" id="button_call">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="image">call_image</property>
-                <property name="label" translatable="yes">C_all</property>
-                <property name="use_underline">True</property>
-              </object>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkButton" id="button_chat">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="has_default">True</property>
-                <property name="image">chat_image</property>
-                <property name="label" translatable="yes">C_hat</property>
-                <property name="use_underline">True</property>
-              </object>
-              <packing>
-                <property name="position">2</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="pack_type">GTK_PACK_END</property>
-          </packing>
-        </child>
+        <property name="xalign">0</property>
+        <property name="label" translatable="yes">Contact ID:</property>
       </object>
+      <packing>
+        <property name="top_attach">1</property>
+        <property name="bottom_attach">2</property>
+        <property name="x_options"></property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkEntry" id="entry_id">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+        <property name="invisible_char">&#x2022;</property>
+        <property name="activates_default">True</property>
+      </object>
+      <packing>
+        <property name="left_attach">1</property>
+        <property name="right_attach">2</property>
+        <property name="top_attach">1</property>
+        <property name="bottom_attach">2</property>
+      </packing>
+    </child>
+    <child>
+      <placeholder/>
     </child>
-    <action-widgets>
-      <action-widget response="-6">button1</action-widget>
-      <action-widget response="1">button_call</action-widget>
-      <action-widget response="2">button_chat</action-widget>
-    </action-widgets>
   </object>
 </interface>
index 3443fd9a099c0f65c80e6b136bc6823947aff25f..17c7f100e303bff989e9c1e7338b22665107e00c 100644 (file)
@@ -314,7 +314,6 @@ empathy_spell_check (const gchar *word)
        spell_setup_languages ();
 
        if (!languages) {
-               DEBUG ("No languages to check against");
                return TRUE;
        }
 
index 3e1015d0c00f8b7f40731b5a489c7cffd94c3cc6..b936416493cdba79db52ab667f1c4d3b54f45f6d 100644 (file)
@@ -47,6 +47,7 @@ typedef struct {
 
        TpChannel      *publish;
        TpChannel      *subscribe;
+       TpChannel      *stored;
        GHashTable     *members; /* handle -> EmpathyContact */
        GHashTable     *pendings; /* handle -> EmpathyContact */
        GHashTable     *groups; /* group name -> TpChannel */
@@ -122,6 +123,75 @@ tp_contact_list_group_invalidated_cb (TpChannel *channel,
        g_hash_table_remove (priv->groups, group_name);
 }
 
+static void
+contacts_added_to_group (EmpathyTpContactList *list,
+                        TpChannel *channel,
+                        GArray *added)
+{
+       EmpathyTpContactListPriv *priv = GET_PRIV (list);
+       const gchar *group_name;
+       guint i;
+
+       group_name = tp_channel_get_identifier (channel);
+
+       for (i = 0; i < added->len; i++) {
+               EmpathyContact *contact;
+               TpHandle handle;
+
+               handle = g_array_index (added, TpHandle, i);
+               contact = g_hash_table_lookup (priv->members,
+                                              GUINT_TO_POINTER (handle));
+               if (contact == NULL) {
+                       continue;
+               }
+
+               DEBUG ("Contact %s (%d) added to group %s",
+                       empathy_contact_get_id (contact), handle, group_name);
+               g_signal_emit_by_name (list, "groups-changed", contact,
+                                      group_name,
+                                      TRUE);
+       }
+}
+
+static void
+tp_contact_list_group_members_changed_cb (TpChannel     *channel,
+                                         gchar         *message,
+                                         GArray        *added,
+                                         GArray        *removed,
+                                         GArray        *local_pending,
+                                         GArray        *remote_pending,
+                                         guint          actor,
+                                         guint          reason,
+                                         EmpathyTpContactList *list)
+{
+       EmpathyTpContactListPriv  *priv = GET_PRIV (list);
+       const gchar *group_name;
+       guint i;
+
+       contacts_added_to_group (list, channel, added);
+
+       group_name = tp_channel_get_identifier (channel);
+
+       for (i = 0; i < removed->len; i++) {
+               EmpathyContact *contact;
+               TpHandle handle;
+
+               handle = g_array_index (removed, TpHandle, i);
+               contact = g_hash_table_lookup (priv->members,
+                                              GUINT_TO_POINTER (handle));
+               if (contact == NULL) {
+                       continue;
+               }
+
+               DEBUG ("Contact %s (%d) removed from group %s",
+                       empathy_contact_get_id (contact), handle, group_name);
+
+               g_signal_emit_by_name (list, "groups-changed", contact,
+                                      group_name,
+                                      FALSE);
+       }
+}
+
 static void
 tp_contact_list_group_ready_cb (TpChannel *channel,
                                const GError *error,
@@ -130,6 +200,8 @@ tp_contact_list_group_ready_cb (TpChannel *channel,
        EmpathyTpContactListPriv *priv = GET_PRIV (list);
        TpChannel *old_group;
        const gchar *group_name;
+       const TpIntSet *members;
+       GArray *arr;
 
        if (error) {
                DEBUG ("Error: %s", error->message);
@@ -158,6 +230,10 @@ tp_contact_list_group_ready_cb (TpChannel *channel,
        g_hash_table_insert (priv->groups, (gpointer) group_name, channel);
        DEBUG ("Group %s added", group_name);
 
+       g_signal_connect (channel, "group-members-changed",
+                         G_CALLBACK (tp_contact_list_group_members_changed_cb),
+                         list);
+
        g_signal_connect (channel, "invalidated",
                          G_CALLBACK (tp_contact_list_group_invalidated_cb),
                          list);
@@ -173,61 +249,13 @@ tp_contact_list_group_ready_cb (TpChannel *channel,
                        g_hash_table_remove (priv->add_to_group, group_name);
                }
        }
-}
 
-static void
-tp_contact_list_group_members_changed_cb (TpChannel     *channel,
-                                         gchar         *message,
-                                         GArray        *added,
-                                         GArray        *removed,
-                                         GArray        *local_pending,
-                                         GArray        *remote_pending,
-                                         guint          actor,
-                                         guint          reason,
-                                         EmpathyTpContactList *list)
-{
-       EmpathyTpContactListPriv  *priv = GET_PRIV (list);
-       const gchar *group_name;
-       guint i;
-
-       group_name = tp_channel_get_identifier (channel);
-
-       for (i = 0; i < added->len; i++) {
-               EmpathyContact *contact;
-               TpHandle handle;
-
-               handle = g_array_index (added, TpHandle, i);
-               contact = g_hash_table_lookup (priv->members,
-                                              GUINT_TO_POINTER (handle));
-               if (contact == NULL) {
-                       continue;
-               }
-
-               DEBUG ("Contact %s (%d) added to group %s",
-                       empathy_contact_get_id (contact), handle, group_name);
-               g_signal_emit_by_name (list, "groups-changed", contact,
-                                      group_name,
-                                      TRUE);
-       }
-
-       for (i = 0; i < removed->len; i++) {
-               EmpathyContact *contact;
-               TpHandle handle;
-
-               handle = g_array_index (removed, TpHandle, i);
-               contact = g_hash_table_lookup (priv->members,
-                                              GUINT_TO_POINTER (handle));
-               if (contact == NULL) {
-                       continue;
-               }
-
-               DEBUG ("Contact %s (%d) removed from group %s",
-                       empathy_contact_get_id (contact), handle, group_name);
-
-               g_signal_emit_by_name (list, "groups-changed", contact,
-                                      group_name,
-                                      FALSE);
-       }
+       /* Get initial members of the group */
+       members = tp_channel_group_get_members (channel);
+       g_assert (members != NULL);
+       arr = tp_intset_to_array (members);
+       contacts_added_to_group (list, channel, arr);
+       g_array_free (arr, TRUE);
 }
 
 static void
@@ -250,11 +278,6 @@ tp_contact_list_group_add_channel (EmpathyTpContactList *list,
                                  object_path, channel_type,
                                  handle_type, handle, NULL);
 
-       /* TpChannel emits initial set of members just before being ready */
-       g_signal_connect (channel, "group-members-changed",
-                         G_CALLBACK (tp_contact_list_group_members_changed_cb),
-                         list);
-
        /* Give the ref to the callback */
        tp_channel_call_when_ready (channel,
                                    tp_contact_list_group_ready_cb,
@@ -485,32 +508,6 @@ tp_contact_list_publish_group_members_changed_cb (TpChannel     *channel,
        }
 }
 
-static void
-tp_contact_list_publish_request_channel_cb (TpConnection *connection,
-                                           const gchar  *object_path,
-                                           const GError *error,
-                                           gpointer      user_data,
-                                           GObject      *list)
-{
-       EmpathyTpContactListPriv *priv = GET_PRIV (list);
-
-       if (error) {
-               DEBUG ("Error: %s", error->message);
-               return;
-       }
-
-       priv->publish = tp_channel_new (connection, object_path,
-                                       TP_IFACE_CHANNEL_TYPE_CONTACT_LIST,
-                                       TP_HANDLE_TYPE_LIST,
-                                       GPOINTER_TO_UINT (user_data),
-                                       NULL);
-
-       /* TpChannel emits initial set of members just before being ready */
-       g_signal_connect (priv->publish, "group-members-changed",
-                         G_CALLBACK (tp_contact_list_publish_group_members_changed_cb),
-                         list);
-}
-
 static void
 tp_contact_list_get_alias_flags_cb (TpConnection *connection,
                                    guint         flags,
@@ -569,31 +566,6 @@ tp_contact_list_get_requestablechannelclasses_cb (TpProxy      *connection,
        }
 }
 
-static void
-tp_contact_list_publish_request_handle_cb (TpConnection *connection,
-                                          const GArray *handles,
-                                          const GError *error,
-                                          gpointer      user_data,
-                                          GObject      *list)
-{
-       TpHandle handle;
-
-       if (error) {
-               DEBUG ("Error: %s", error->message);
-               return;
-       }
-
-       handle = g_array_index (handles, TpHandle, 0);
-       tp_cli_connection_call_request_channel (connection, -1,
-                                               TP_IFACE_CHANNEL_TYPE_CONTACT_LIST,
-                                               TP_HANDLE_TYPE_LIST,
-                                               handle,
-                                               TRUE,
-                                               tp_contact_list_publish_request_channel_cb,
-                                               GUINT_TO_POINTER (handle), NULL,
-                                               list);
-}
-
 static void
 tp_contact_list_subscribe_group_members_changed_cb (TpChannel     *channel,
                                                    gchar         *message,
@@ -632,57 +604,6 @@ tp_contact_list_subscribe_group_members_changed_cb (TpChannel     *channel,
        }
 }
 
-static void
-tp_contact_list_subscribe_request_channel_cb (TpConnection *connection,
-                                             const gchar  *object_path,
-                                             const GError *error,
-                                             gpointer      user_data,
-                                             GObject      *list)
-{
-       EmpathyTpContactListPriv *priv = GET_PRIV (list);
-
-       if (error) {
-               DEBUG ("Error: %s", error->message);
-               return;
-       }
-
-       priv->subscribe = tp_channel_new (connection, object_path,
-                                         TP_IFACE_CHANNEL_TYPE_CONTACT_LIST,
-                                         TP_HANDLE_TYPE_LIST,
-                                         GPOINTER_TO_UINT (user_data),
-                                         NULL);
-
-       /* TpChannel emits initial set of members just before being ready */
-       g_signal_connect (priv->subscribe, "group-members-changed",
-                         G_CALLBACK (tp_contact_list_subscribe_group_members_changed_cb),
-                         list);
-}
-
-static void
-tp_contact_list_subscribe_request_handle_cb (TpConnection *connection,
-                                            const GArray *handles,
-                                            const GError *error,
-                                            gpointer      user_data,
-                                            GObject      *list)
-{
-       TpHandle handle;
-
-       if (error) {
-               DEBUG ("Error: %s", error->message);
-               return;
-       }
-
-       handle = g_array_index (handles, TpHandle, 0);
-       tp_cli_connection_call_request_channel (connection, -1,
-                                               TP_IFACE_CHANNEL_TYPE_CONTACT_LIST,
-                                               TP_HANDLE_TYPE_LIST,
-                                               handle,
-                                               TRUE,
-                                               tp_contact_list_subscribe_request_channel_cb,
-                                               GUINT_TO_POINTER (handle), NULL,
-                                               list);
-}
-
 static void
 tp_contact_list_new_channel_cb (TpConnection *proxy,
                                const gchar  *object_path,
@@ -750,6 +671,9 @@ tp_contact_list_finalize (GObject *object)
        if (priv->publish) {
                g_object_unref (priv->publish);
        }
+       if (priv->stored) {
+               g_object_unref (priv->stored);
+       }
 
        if (priv->connection) {
                g_object_unref (priv->connection);
@@ -774,12 +698,92 @@ tp_contact_list_finalize (GObject *object)
 }
 
 static void
-tp_contact_list_constructed (GObject *list)
+list_ensure_channel_cb (TpConnection *conn,
+                       gboolean yours,
+                       const gchar *path,
+                       GHashTable *properties,
+                       const GError *error,
+                       gpointer user_data,
+                       GObject *weak_object)
 {
+       EmpathyTpContactList *list = user_data;
+       EmpathyTpContactListPriv *priv = GET_PRIV (list);
+       const gchar *id;
+       TpChannel *channel;
+
+       if (error != NULL) {
+               DEBUG ("failed: %s\n", error->message);
+               return;
+       }
 
+       /* We requested that channel by providing TargetID property, so it's
+        * guaranteed that tp_channel_get_identifier will return it. */
+       channel = tp_channel_new_from_properties (conn, path, properties, NULL);
+       id = tp_channel_get_identifier (channel);
+
+       /* TpChannel emits initial set of members just before being ready */
+       if (!tp_strdiff (id, "stored")) {
+               priv->stored = channel;
+       } else if (!tp_strdiff (id, "publish")) {
+               priv->publish = channel;
+               g_signal_connect (priv->publish, "group-members-changed",
+                                 G_CALLBACK (tp_contact_list_publish_group_members_changed_cb),
+                                 list);
+       } else if (!tp_strdiff (id, "subscribe")) {
+               priv->subscribe = channel;
+               g_signal_connect (priv->subscribe, "group-members-changed",
+                                 G_CALLBACK (tp_contact_list_subscribe_group_members_changed_cb),
+                                 list);
+       } else {
+               g_warn_if_reached ();
+               g_object_unref (channel);
+       }
+}
+
+static void
+conn_ready_cb (TpConnection *connection,
+              const GError *error,
+              gpointer data)
+{
+       EmpathyTpContactList *list = data;
+       EmpathyTpContactListPriv *priv = GET_PRIV (list);
+       GHashTable *request;
+
+       if (error != NULL) {
+               DEBUG ("failed: %s", error->message);
+               goto out;
+       }
+
+       request = tp_asv_new (
+               TP_IFACE_CHANNEL ".ChannelType", G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST,
+               TP_IFACE_CHANNEL ".TargetHandleType", G_TYPE_UINT, TP_HANDLE_TYPE_LIST,
+               NULL);
+
+       /* Request the 'stored' list. */
+       tp_asv_set_static_string (request, TP_IFACE_CHANNEL ".TargetID", "stored");
+       tp_cli_connection_interface_requests_call_ensure_channel (priv->connection,
+               -1, request, list_ensure_channel_cb, list, NULL, G_OBJECT (list));
+
+       /* Request the 'publish' list. */
+       tp_asv_set_static_string (request, TP_IFACE_CHANNEL ".TargetID", "publish");
+       tp_cli_connection_interface_requests_call_ensure_channel (priv->connection,
+               -1, request, list_ensure_channel_cb, list, NULL, G_OBJECT (list));
+
+       /* Request the 'subscribe' list. */
+       tp_asv_set_static_string (request, TP_IFACE_CHANNEL ".TargetID", "subscribe");
+       tp_cli_connection_interface_requests_call_ensure_channel (priv->connection,
+               -1, request, list_ensure_channel_cb, list, NULL, G_OBJECT (list));
+
+       g_hash_table_unref (request);
+out:
+       g_object_unref (list);
+}
+
+static void
+tp_contact_list_constructed (GObject *list)
+{
        EmpathyTpContactListPriv *priv = GET_PRIV (list);
        gchar                    *protocol_name = NULL;
-       const gchar              *names[] = {NULL, NULL};
 
        priv->factory = empathy_tp_contact_factory_dup_singleton (priv->connection);
 
@@ -809,22 +813,8 @@ tp_contact_list_constructed (GObject *list)
                priv->flags |= EMPATHY_CONTACT_LIST_CAN_GROUP;
        }
 
-       names[0] = "publish";
-       tp_cli_connection_call_request_handles (priv->connection,
-                                               -1,
-                                               TP_HANDLE_TYPE_LIST,
-                                               names,
-                                               tp_contact_list_publish_request_handle_cb,
-                                               NULL, NULL,
-                                               G_OBJECT (list));
-       names[0] = "subscribe";
-       tp_cli_connection_call_request_handles (priv->connection,
-                                               -1,
-                                               TP_HANDLE_TYPE_LIST,
-                                               names,
-                                               tp_contact_list_subscribe_request_handle_cb,
-                                               NULL, NULL,
-                                               G_OBJECT (list));
+       tp_connection_call_when_ready (priv->connection, conn_ready_cb,
+               g_object_ref (list));
 
        tp_cli_connection_call_list_channels (priv->connection, -1,
                                              tp_contact_list_list_channels_cb,
@@ -994,6 +984,16 @@ tp_contact_list_remove (EmpathyContactList *list,
        GArray handles = {(gchar *) &handle, 1};
 
        handle = empathy_contact_get_handle (contact);
+
+       /* FIXME: this is racy if tp_contact_list_remove is called before the
+        * 'stored' list has been retrieved. */
+       if (priv->stored != NULL) {
+               tp_cli_channel_interface_group_call_remove_members (priv->stored,
+                       -1, &handles, message, NULL, NULL, NULL, NULL);
+               /* Contact will be removed from 'publish' and 'subscribe' too */
+               return;
+       }
+
        if (priv->subscribe) {
                tp_cli_channel_interface_group_call_remove_members (priv->subscribe,
                        -1, &handles, message, NULL, NULL, NULL, NULL);