* 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 <glib/gi18n-lib.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
};
EmpathyIndividualStore *store;
EmpathyIndividualView *view;
GtkWidget *search_entry;
+ GtkWidget *scroll_view;
GPtrArray *search_words;
gchar *search_str;
EmpathyContactChooserFilterFunc filter_func;
gpointer filter_data;
+
+ /* list of reffed TpContact */
+ GList *tp_contacts;
};
struct _AddTemporaryIndividualCtx
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);
}
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
}
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)
+{
+ empathy_individual_view_refilter (self->priv->view);
+}
+
+static void
+get_contacts_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
- EmpathyContactChooser *self =
- (EmpathyContactChooser *) weak_object;
- AddTemporaryIndividualCtx *ctx = user_data;
- TpAccount *account;
- TpfPersonaStore *store;
+ 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
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
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);
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,
tp_proxy_prepare_async (self->priv->account_mgr, features, NULL, NULL);
/* Search entry */
- self->priv->search_entry = gtk_entry_new ();
- gtk_box_pack_start (GTK_BOX (self), self->priv->search_entry, FALSE, TRUE, 6);
+ self->priv->search_entry = gtk_search_entry_new ();
+ gtk_entry_set_placeholder_text ( GTK_ENTRY(self->priv->search_entry),
+ _("Type to search a contact…"));
+ gtk_box_pack_start (GTK_BOX (self), self->priv->search_entry, FALSE, TRUE, 0);
gtk_widget_show (self->priv->search_entry);
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);
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, 0);
gtk_widget_show (GTK_WIDGET (self->priv->view));
- gtk_widget_show (scroll);
+ gtk_widget_show (self->priv->scroll_view);
}
GtkWidget *
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)
{
{
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);
+}