2 * empathy-contact-chooser.c
4 * EmpathyContactChooser
6 * (c) 2009, Collabora Ltd.
9 * Danielle Madeley <danielle.madeley@collabora.co.uk>
12 #include <glib/gi18n.h>
13 #include <folks/folks-telepathy.h>
15 #include "empathy-contact-chooser.h"
17 #include <libempathy-gtk/empathy-individual-view.h>
18 #include <libempathy-gtk/empathy-ui-utils.h>
20 G_DEFINE_TYPE (EmpathyContactChooser,
21 empathy_contact_chooser, GTK_TYPE_BOX);
24 SIG_SELECTION_CHANGED,
28 static guint signals[LAST_SIGNAL];
30 typedef struct _AddTemporaryIndividualCtx AddTemporaryIndividualCtx;
32 struct _EmpathyContactChooserPrivate
34 TpAccountManager *account_mgr;
36 EmpathyIndividualStore *store;
37 EmpathyIndividualView *view;
38 GtkWidget *search_entry;
40 GPtrArray *search_words;
43 /* Context representing the FolksIndividual which are added because of the
44 * current search from the user. */
45 AddTemporaryIndividualCtx *add_temp_ctx;
47 EmpathyContactChooserFilterFunc filter_func;
51 struct _AddTemporaryIndividualCtx
53 EmpathyContactChooser *self;
54 /* List of owned FolksIndividual */
58 static AddTemporaryIndividualCtx *
59 add_temporary_individual_ctx_new (EmpathyContactChooser *self)
61 AddTemporaryIndividualCtx *ctx = g_slice_new0 (AddTemporaryIndividualCtx);
68 add_temporary_individual_ctx_free (AddTemporaryIndividualCtx *ctx)
72 /* Remove all the individuals from the model */
73 for (l = ctx->individuals; l != NULL; l = g_list_next (l))
75 FolksIndividual *individual = l->data;
77 individual_store_remove_individual_and_disconnect (ctx->self->priv->store,
80 g_object_unref (individual);
83 g_list_free (ctx->individuals);
84 g_slice_free (AddTemporaryIndividualCtx, ctx);
88 contact_chooser_dispose (GObject *object)
90 EmpathyContactChooser *self = (EmpathyContactChooser *)
93 tp_clear_pointer (&self->priv->add_temp_ctx,
94 add_temporary_individual_ctx_free);
96 tp_clear_object (&self->priv->store);
97 tp_clear_pointer (&self->priv->search_words, g_ptr_array_unref);
98 tp_clear_pointer (&self->priv->search_str, g_free);
100 tp_clear_object (&self->priv->account_mgr);
102 G_OBJECT_CLASS (empathy_contact_chooser_parent_class)->dispose (
107 empathy_contact_chooser_class_init (
108 EmpathyContactChooserClass *klass)
110 GObjectClass *object_class = G_OBJECT_CLASS (klass);
112 object_class->dispose = contact_chooser_dispose;
114 g_type_class_add_private (object_class,
115 sizeof (EmpathyContactChooserPrivate));
117 signals[SIG_SELECTION_CHANGED] = g_signal_new ("selection-changed",
118 G_TYPE_FROM_CLASS (klass),
122 g_cclosure_marshal_generic,
124 1, FOLKS_TYPE_INDIVIDUAL);
128 view_selection_changed_cb (GtkWidget *treeview,
129 EmpathyContactChooser *self)
131 FolksIndividual *individual;
133 individual = empathy_individual_view_dup_selected (self->priv->view);
135 g_signal_emit (self, signals[SIG_SELECTION_CHANGED], 0, individual);
137 tp_clear_object (&individual);
141 filter_func (GtkTreeModel *model,
145 EmpathyContactChooser *self = user_data;
146 FolksIndividual *individual;
148 gboolean display = FALSE;
149 gboolean searching = FALSE;
151 gtk_tree_model_get (model, iter,
152 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual,
153 EMPATHY_INDIVIDUAL_STORE_COL_IS_ONLINE, &is_online,
156 if (individual == NULL)
159 if (self->priv->search_words != NULL)
163 /* Filter out the contact if we are searching and it doesn't match */
164 if (!empathy_individual_match_string (individual,
165 self->priv->search_str, self->priv->search_words))
169 if (self->priv->filter_func == NULL)
172 display = self->priv->filter_func (self, individual, is_online, searching,
173 self->priv->filter_data);
175 tp_clear_object (&individual);
180 get_contacts_cb (TpConnection *connection,
182 TpContact * const *contacts,
183 const gchar * const *requested_ids,
184 GHashTable *failed_id_errors,
187 GObject *weak_object)
189 EmpathyContactChooser *self =
190 (EmpathyContactChooser *) weak_object;
191 AddTemporaryIndividualCtx *ctx = user_data;
193 TpfPersonaStore *store;
194 FolksIndividual *individual;
195 TpfPersona *persona_new;
198 if (self->priv->add_temp_ctx != ctx)
199 /* another request has been started */
205 account = tp_connection_get_account (connection);
207 store = tpf_persona_store_new (account);
209 gee_hash_set_new (FOLKS_TYPE_PERSONA, g_object_ref, g_object_unref,
210 g_direct_hash, g_direct_equal));
211 persona_new = tpf_persona_new (contacts[0], store);
212 gee_collection_add (GEE_COLLECTION (personas),
213 tpf_persona_new (contacts[0], store));
215 individual = folks_individual_new (personas);
217 /* Pass ownership to the list */
218 ctx->individuals = g_list_prepend (ctx->individuals, individual);
220 individual_store_add_individual_and_connect (self->priv->store, individual);
222 g_clear_object (&persona_new);
223 g_clear_object (&personas);
224 g_object_unref (store);
228 add_temporary_individuals (EmpathyContactChooser *self,
233 tp_clear_pointer (&self->priv->add_temp_ctx,
234 add_temporary_individual_ctx_free);
236 if (tp_str_empty (id))
239 self->priv->add_temp_ctx = add_temporary_individual_ctx_new (self);
241 /* Try to add an individual for each connected account */
242 accounts = tp_account_manager_get_valid_accounts (self->priv->account_mgr);
243 for (l = accounts; l != NULL; l = g_list_next (l))
245 TpAccount *account = l->data;
247 TpContactFeature features[] = { TP_CONTACT_FEATURE_ALIAS,
248 TP_CONTACT_FEATURE_AVATAR_DATA,
249 TP_CONTACT_FEATURE_PRESENCE,
250 TP_CONTACT_FEATURE_CAPABILITIES };
252 conn = tp_account_get_connection (account);
256 tp_connection_get_contacts_by_id (conn, 1, &id, G_N_ELEMENTS (features),
257 features, get_contacts_cb, self->priv->add_temp_ctx, NULL,
261 g_list_free (accounts);
265 search_text_changed (GtkEntry *entry,
266 EmpathyContactChooser *self)
270 tp_clear_pointer (&self->priv->search_words, g_ptr_array_unref);
271 tp_clear_pointer (&self->priv->search_str, g_free);
273 id = gtk_entry_get_text (entry);
275 self->priv->search_words = empathy_live_search_strip_utf8_string (id);
276 self->priv->search_str = g_strdup (id);
278 add_temporary_individuals (self, id);
280 empathy_individual_view_refilter (self->priv->view);
284 empathy_contact_chooser_init (EmpathyContactChooser *self)
286 EmpathyIndividualManager *mgr;
287 GtkTreeSelection *selection;
289 GQuark features[] = { TP_ACCOUNT_MANAGER_FEATURE_CORE, 0 };
291 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EMPATHY_TYPE_CONTACT_CHOOSER,
292 EmpathyContactChooserPrivate);
294 self->priv->account_mgr = tp_account_manager_dup ();
296 /* We don't wait for the CORE feature to be prepared, which is fine as we
297 * won't use the account manager until user starts searching. Furthermore,
298 * the AM has probably already been prepared by another Empathy
300 tp_proxy_prepare_async (self->priv->account_mgr, features, NULL, NULL);
303 self->priv->search_entry = gtk_entry_new ();
304 gtk_box_pack_start (GTK_BOX (self), self->priv->search_entry, FALSE, TRUE, 6);
305 gtk_widget_show (self->priv->search_entry);
307 g_signal_connect (self->priv->search_entry, "changed",
308 G_CALLBACK (search_text_changed), self);
310 /* Add the treeview */
311 mgr = empathy_individual_manager_dup_singleton ();
312 self->priv->store = empathy_individual_store_new (mgr);
313 g_object_unref (mgr);
315 empathy_individual_store_set_show_groups (self->priv->store, FALSE);
317 self->priv->view = empathy_individual_view_new (self->priv->store,
318 EMPATHY_INDIVIDUAL_VIEW_FEATURE_NONE, EMPATHY_INDIVIDUAL_FEATURE_NONE);
320 empathy_individual_view_set_custom_filter (self->priv->view,
323 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->view));
325 g_signal_connect (selection, "changed",
326 G_CALLBACK (view_selection_changed_cb), self);
328 scroll = gtk_scrolled_window_new (NULL, NULL);
330 gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (self->priv->view));
332 gtk_box_pack_start (GTK_BOX (self), scroll, TRUE, TRUE, 6);
333 gtk_widget_show (GTK_WIDGET (self->priv->view));
334 gtk_widget_show (scroll);
338 empathy_contact_chooser_new (void)
340 return g_object_new (EMPATHY_TYPE_CONTACT_CHOOSER,
341 "orientation", GTK_ORIENTATION_VERTICAL,
346 empathy_contact_chooser_dup_selected (EmpathyContactChooser *self)
348 return empathy_individual_view_dup_selected (self->priv->view);
352 empathy_contact_chooser_set_filter_func (EmpathyContactChooser *self,
353 EmpathyContactChooserFilterFunc func,
356 g_assert (self->priv->filter_func == NULL);
358 self->priv->filter_func = func;
359 self->priv->filter_data = user_data;
363 empathy_contact_chooser_show_search_entry (EmpathyContactChooser *self,
366 gtk_widget_set_visible (self->priv->search_entry, show);