]> git.0d.be Git - empathy.git/blobdiff - libempathy-gtk/empathy-contact-chooser.c
libempathy-gtk: fix uninitialized variable
[empathy.git] / libempathy-gtk / empathy-contact-chooser.c
index 10a3757a3edfb0dd3b202893d623ed165b695402..05dbe3449a21baa7bc89a2e8e821e22d93e700bf 100644 (file)
@@ -9,19 +9,21 @@
  *    Danielle Madeley <danielle.madeley@collabora.co.uk>
  */
 
-#include <glib/gi18n.h>
-#include <folks/folks-telepathy.h>
-
+#include "config.h"
 #include "empathy-contact-chooser.h"
 
-#include <libempathy-gtk/empathy-individual-view.h>
-#include <libempathy-gtk/empathy-ui-utils.h>
+#include "empathy-client-factory.h"
+#include "empathy-individual-store-manager.h"
+#include "empathy-individual-view.h"
+#include "empathy-ui-utils.h"
+#include "empathy-utils.h"
 
 G_DEFINE_TYPE (EmpathyContactChooser,
     empathy_contact_chooser, GTK_TYPE_BOX);
 
 enum {
   SIG_SELECTION_CHANGED,
+  SIG_ACTIVATE,
   LAST_SIGNAL
 };
 
@@ -36,6 +38,7 @@ struct _EmpathyContactChooserPrivate
   EmpathyIndividualStore *store;
   EmpathyIndividualView *view;
   GtkWidget *search_entry;
+  GtkWidget *scroll_view;
 
   GPtrArray *search_words;
   gchar *search_str;
@@ -46,6 +49,9 @@ struct _EmpathyContactChooserPrivate
 
   EmpathyContactChooserFilterFunc filter_func;
   gpointer filter_data;
+
+  /* list of reffed TpContact */
+  GList *tp_contacts;
 };
 
 struct _AddTemporaryIndividualCtx
@@ -99,6 +105,9 @@ contact_chooser_dispose (GObject *object)
 
   tp_clear_object (&self->priv->account_mgr);
 
+  g_list_free_full (self->priv->tp_contacts, g_object_unref);
+  self->priv->tp_contacts = NULL;
+
   G_OBJECT_CLASS (empathy_contact_chooser_parent_class)->dispose (
       object);
 }
@@ -122,6 +131,15 @@ empathy_contact_chooser_class_init (
             g_cclosure_marshal_generic,
             G_TYPE_NONE,
             1, FOLKS_TYPE_INDIVIDUAL);
+
+  signals[SIG_ACTIVATE] = g_signal_new ("activate",
+            G_TYPE_FROM_CLASS (klass),
+            G_SIGNAL_RUN_LAST,
+            0,
+            NULL, NULL,
+            g_cclosure_marshal_generic,
+            G_TYPE_NONE,
+            0);
 }
 
 static void
@@ -177,51 +195,72 @@ out:
 }
 
 static void
-get_contacts_cb (TpConnection *connection,
-    guint n_contacts,
-    TpContact * const *contacts,
-    const gchar * const *requested_ids,
-    GHashTable *failed_id_errors,
-    const GError *error,
-    gpointer user_data,
-    GObject *weak_object)
+contact_capabilities_changed (TpContact *contact,
+    GParamSpec *pspec,
+    EmpathyContactChooser *self)
 {
-  EmpathyContactChooser *self =
-    (EmpathyContactChooser *) weak_object;
-  AddTemporaryIndividualCtx *ctx = user_data;
-  TpAccount *account;
-  TpfPersonaStore *store;
+  empathy_individual_view_refilter (self->priv->view);
+}
+
+static void
+get_contacts_cb (GObject *source,
+    GAsyncResult *result,
+    gpointer user_data)
+{
+  TpWeakRef *wr = user_data;
+  AddTemporaryIndividualCtx *ctx;
+  EmpathyContactChooser *self;
+  GError *error = NULL;
   FolksIndividual *individual;
-  TpfPersona *persona_new;
-  GeeSet *personas;
+  TpContact *contact;
+  EmpathyContact *emp_contact = NULL;
+
+  self = tp_weak_ref_dup_object (wr);
+  if (self == NULL)
+    goto out;
+
+  ctx = tp_weak_ref_get_user_data (wr);
+
+  emp_contact = empathy_client_factory_dup_contact_by_id_finish (
+        EMPATHY_CLIENT_FACTORY (source), result, &error);
+  if (emp_contact == NULL)
+    goto out;
+
+  contact = empathy_contact_get_tp_contact (emp_contact);
 
   if (self->priv->add_temp_ctx != ctx)
     /* another request has been started */
-    return;
-
-  if (n_contacts != 1)
-    return;
+    goto out;
 
-  account = tp_connection_get_account (connection);
+  individual =  empathy_ensure_individual_from_tp_contact (contact);
+  if (individual == NULL)
+    goto out;
 
-  store = tpf_persona_store_new (account);
-  personas = GEE_SET (
-      gee_hash_set_new (FOLKS_TYPE_PERSONA, g_object_ref, g_object_unref,
-      g_direct_hash, g_direct_equal));
-  persona_new = tpf_persona_new (contacts[0], store);
-  gee_collection_add (GEE_COLLECTION (personas),
-      tpf_persona_new (contacts[0], store));
+  /* tp-glib will unref the TpContact once we return from this callback
+   * but folks expect us to keep a reference on the TpContact.
+   * Ideally folks shouldn't force us to do that: bgo #666580 */
+  self->priv->tp_contacts = g_list_prepend (self->priv->tp_contacts,
+      g_object_ref (contact));
 
-  individual = folks_individual_new (personas);
+  /* listen for updates to the capabilities */
+  tp_g_signal_connect_object (contact, "notify::capabilities",
+      G_CALLBACK (contact_capabilities_changed), self, 0);
 
   /* Pass ownership to the list */
   ctx->individuals = g_list_prepend (ctx->individuals, individual);
 
   individual_store_add_individual_and_connect (self->priv->store, individual);
 
-  g_clear_object (&persona_new);
-  g_clear_object (&personas);
-  g_object_unref (store);
+  /* if nothing is selected, select the first matching node */
+  if (!gtk_tree_selection_get_selected (
+        gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->view)),
+        NULL, NULL))
+    empathy_individual_view_select_first (self->priv->view);
+
+out:
+  g_clear_object (&emp_contact);
+  g_clear_object (&self);
+  tp_weak_ref_destroy (wr);
 }
 
 static void
@@ -239,26 +278,27 @@ add_temporary_individuals (EmpathyContactChooser *self,
   self->priv->add_temp_ctx = add_temporary_individual_ctx_new (self);
 
   /* Try to add an individual for each connected account */
-  accounts = tp_account_manager_get_valid_accounts (self->priv->account_mgr);
+  accounts = tp_account_manager_dup_valid_accounts (self->priv->account_mgr);
   for (l = accounts; l != NULL; l = g_list_next (l))
     {
       TpAccount *account = l->data;
       TpConnection *conn;
-      TpContactFeature features[] = { TP_CONTACT_FEATURE_ALIAS,
-          TP_CONTACT_FEATURE_AVATAR_DATA,
-          TP_CONTACT_FEATURE_PRESENCE,
-          TP_CONTACT_FEATURE_CAPABILITIES };
+      EmpathyClientFactory *factory;
 
       conn = tp_account_get_connection (account);
       if (conn == NULL)
         continue;
 
-      tp_connection_get_contacts_by_id (conn, 1, &id, G_N_ELEMENTS (features),
-          features, get_contacts_cb, self->priv->add_temp_ctx, NULL,
-          G_OBJECT (self));
+      factory = empathy_client_factory_dup ();
+
+      empathy_client_factory_dup_contact_by_id_async (factory, conn, id,
+          get_contacts_cb,
+          tp_weak_ref_new (self, self->priv->add_temp_ctx, NULL));
+
+      g_object_unref (factory);
     }
 
-  g_list_free (accounts);
+  g_list_free_full (accounts, g_object_unref);
 }
 
 static void
@@ -272,7 +312,7 @@ search_text_changed (GtkEntry *entry,
 
   id = gtk_entry_get_text (entry);
 
-  self->priv->search_words = empathy_live_search_strip_utf8_string (id);
+  self->priv->search_words = tpaw_live_search_strip_utf8_string (id);
   self->priv->search_str = g_strdup (id);
 
   add_temporary_individuals (self, id);
@@ -280,12 +320,81 @@ search_text_changed (GtkEntry *entry,
   empathy_individual_view_refilter (self->priv->view);
 }
 
+static void
+search_activate_cb (GtkEntry *entry,
+    EmpathyContactChooser *self)
+{
+  g_signal_emit (self, signals[SIG_ACTIVATE], 0);
+}
+
+static void
+view_activate_cb (GtkTreeView *view,
+    GtkTreePath *path,
+    GtkTreeViewColumn *column,
+    EmpathyContactChooser *self)
+{
+  g_signal_emit (self, signals[SIG_ACTIVATE], 0);
+}
+
+static gboolean
+search_key_press_cb (GtkEntry *entry,
+    GdkEventKey *event,
+    EmpathyContactChooser *self)
+{
+  GtkTreeSelection *selection;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  if (event->state != 0)
+    return FALSE;
+
+  switch (event->keyval)
+    {
+      case GDK_KEY_Down:
+      case GDK_KEY_KP_Down:
+      case GDK_KEY_Up:
+      case GDK_KEY_KP_Up:
+        break;
+
+      default:
+        return FALSE;
+    }
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->view));
+
+  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+    return TRUE;
+
+  switch (event->keyval)
+    {
+      case GDK_KEY_Down:
+      case GDK_KEY_KP_Down:
+        if (!gtk_tree_model_iter_next (model, &iter))
+          return TRUE;
+
+        break;
+
+      case GDK_KEY_Up:
+      case GDK_KEY_KP_Up:
+        if (!gtk_tree_model_iter_previous (model, &iter))
+          return TRUE;
+
+        break;
+
+      default:
+        g_assert_not_reached ();
+    }
+
+  gtk_tree_selection_select_iter (selection, &iter);
+
+  return TRUE;
+}
+
 static void
 empathy_contact_chooser_init (EmpathyContactChooser *self)
 {
   EmpathyIndividualManager *mgr;
   GtkTreeSelection *selection;
-  GtkWidget *scroll;
   GQuark features[] = { TP_ACCOUNT_MANAGER_FEATURE_CORE, 0 };
 
   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EMPATHY_TYPE_CONTACT_CHOOSER,
@@ -306,10 +415,15 @@ empathy_contact_chooser_init (EmpathyContactChooser *self)
 
   g_signal_connect (self->priv->search_entry, "changed",
       G_CALLBACK (search_text_changed), self);
+  g_signal_connect (self->priv->search_entry, "activate",
+      G_CALLBACK (search_activate_cb), self);
+  g_signal_connect (self->priv->search_entry, "key-press-event",
+      G_CALLBACK (search_key_press_cb), self);
 
   /* Add the treeview */
   mgr = empathy_individual_manager_dup_singleton ();
-  self->priv->store = empathy_individual_store_new (mgr);
+  self->priv->store = EMPATHY_INDIVIDUAL_STORE (
+      empathy_individual_store_manager_new (mgr));
   g_object_unref (mgr);
 
   empathy_individual_store_set_show_groups (self->priv->store, FALSE);
@@ -324,14 +438,17 @@ empathy_contact_chooser_init (EmpathyContactChooser *self)
 
   g_signal_connect (selection, "changed",
       G_CALLBACK (view_selection_changed_cb), self);
+  g_signal_connect (self->priv->view, "row-activated",
+      G_CALLBACK (view_activate_cb), self);
 
-  scroll = gtk_scrolled_window_new (NULL, NULL);
+  self->priv->scroll_view = gtk_scrolled_window_new (NULL, NULL);
 
-  gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (self->priv->view));
+  gtk_container_add (GTK_CONTAINER (self->priv->scroll_view),
+      GTK_WIDGET (self->priv->view));
 
-  gtk_box_pack_start (GTK_BOX (self), scroll, TRUE, TRUE, 6);
+  gtk_box_pack_start (GTK_BOX (self), self->priv->scroll_view, TRUE, TRUE, 6);
   gtk_widget_show (GTK_WIDGET (self->priv->view));
-  gtk_widget_show (scroll);
+  gtk_widget_show (self->priv->scroll_view);
 }
 
 GtkWidget *
@@ -342,6 +459,9 @@ empathy_contact_chooser_new (void)
       NULL);
 }
 
+/* Note that because of bgo #666580 the returned invidivdual is valid until
+ * @self is destroyed. To keep it around after that, the TpContact associated
+ * with the individual needs to be manually reffed. */
 FolksIndividual *
 empathy_contact_chooser_dup_selected (EmpathyContactChooser *self)
 {
@@ -365,3 +485,10 @@ empathy_contact_chooser_show_search_entry (EmpathyContactChooser *self,
 {
   gtk_widget_set_visible (self->priv->search_entry, show);
 }
+
+void
+empathy_contact_chooser_show_tree_view (EmpathyContactChooser *self,
+    gboolean show)
+{
+  gtk_widget_set_visible (GTK_WIDGET (self->priv->scroll_view), show);
+}