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;
39 GPtrArray *search_words;
42 /* Context representing the FolksIndividual which are added because of the
43 * current search from the user. */
44 AddTemporaryIndividualCtx *add_temp_ctx;
46 EmpathyContactChooserFilterFunc filter_func;
50 struct _AddTemporaryIndividualCtx
52 EmpathyContactChooser *self;
53 /* List of owned FolksIndividual */
57 static AddTemporaryIndividualCtx *
58 add_temporary_individual_ctx_new (EmpathyContactChooser *self)
60 AddTemporaryIndividualCtx *ctx = g_slice_new0 (AddTemporaryIndividualCtx);
67 add_temporary_individual_ctx_free (AddTemporaryIndividualCtx *ctx)
71 /* Remove all the individuals from the model */
72 for (l = ctx->individuals; l != NULL; l = g_list_next (l))
74 FolksIndividual *individual = l->data;
76 individual_store_remove_individual_and_disconnect (ctx->self->priv->store,
79 g_object_unref (individual);
82 g_list_free (ctx->individuals);
83 g_slice_free (AddTemporaryIndividualCtx, ctx);
87 contact_chooser_dispose (GObject *object)
89 EmpathyContactChooser *self = (EmpathyContactChooser *)
92 tp_clear_pointer (&self->priv->add_temp_ctx,
93 add_temporary_individual_ctx_free);
95 tp_clear_object (&self->priv->store);
96 tp_clear_pointer (&self->priv->search_words, g_ptr_array_unref);
97 tp_clear_pointer (&self->priv->search_str, g_free);
99 tp_clear_object (&self->priv->account_mgr);
101 G_OBJECT_CLASS (empathy_contact_chooser_parent_class)->dispose (
106 empathy_contact_chooser_class_init (
107 EmpathyContactChooserClass *klass)
109 GObjectClass *object_class = G_OBJECT_CLASS (klass);
111 object_class->dispose = contact_chooser_dispose;
113 g_type_class_add_private (object_class,
114 sizeof (EmpathyContactChooserPrivate));
116 signals[SIG_SELECTION_CHANGED] = g_signal_new ("selection-changed",
117 G_TYPE_FROM_CLASS (klass),
121 g_cclosure_marshal_VOID__OBJECT,
123 1, FOLKS_TYPE_INDIVIDUAL);
127 view_selection_changed_cb (GtkWidget *treeview,
128 EmpathyContactChooser *self)
130 FolksIndividual *individual;
132 individual = empathy_individual_view_dup_selected (self->priv->view);
134 g_signal_emit (self, signals[SIG_SELECTION_CHANGED], 0, individual);
136 tp_clear_object (&individual);
140 filter_func (GtkTreeModel *model,
144 EmpathyContactChooser *self = user_data;
145 FolksIndividual *individual;
147 gboolean display = FALSE;
148 gboolean searching = FALSE;
150 gtk_tree_model_get (model, iter,
151 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual,
152 EMPATHY_INDIVIDUAL_STORE_COL_IS_ONLINE, &is_online,
155 if (individual == NULL)
158 if (self->priv->search_words != NULL)
162 /* Filter out the contact if we are searching and it doesn't match */
163 if (!empathy_individual_match_string (individual,
164 self->priv->search_str, self->priv->search_words))
168 if (self->priv->filter_func == NULL)
171 display = self->priv->filter_func (self, individual, is_online, searching,
172 self->priv->filter_data);
174 tp_clear_object (&individual);
179 get_contacts_cb (TpConnection *connection,
181 TpContact * const *contacts,
182 const gchar * const *requested_ids,
183 GHashTable *failed_id_errors,
186 GObject *weak_object)
188 EmpathyContactChooser *self =
189 (EmpathyContactChooser *) weak_object;
190 AddTemporaryIndividualCtx *ctx = user_data;
192 TpfPersonaStore *store;
193 FolksIndividual *individual;
194 TpfPersona *persona_new;
197 if (self->priv->add_temp_ctx != ctx)
198 /* another request has been started */
204 account = g_object_get_data (G_OBJECT (connection), "account");
206 store = tpf_persona_store_new (account);
208 gee_hash_set_new (FOLKS_TYPE_PERSONA, g_object_ref, g_object_unref,
209 g_direct_hash, g_direct_equal));
210 persona_new = tpf_persona_new (contacts[0], store);
211 gee_collection_add (GEE_COLLECTION (personas),
212 tpf_persona_new (contacts[0], store));
214 individual = folks_individual_new (personas);
216 /* Pass ownership to the list */
217 ctx->individuals = g_list_prepend (ctx->individuals, individual);
219 individual_store_add_individual_and_connect (self->priv->store, individual);
221 g_clear_object (&persona_new);
222 g_clear_object (&personas);
223 g_object_unref (store);
227 add_temporary_individuals (EmpathyContactChooser *self,
232 tp_clear_pointer (&self->priv->add_temp_ctx,
233 add_temporary_individual_ctx_free);
235 if (tp_str_empty (id))
238 self->priv->add_temp_ctx = add_temporary_individual_ctx_new (self);
240 /* Try to add an individual for each connected account */
241 accounts = tp_account_manager_get_valid_accounts (self->priv->account_mgr);
242 for (l = accounts; l != NULL; l = g_list_next (l))
244 TpAccount *account = l->data;
246 TpContactFeature features[] = { TP_CONTACT_FEATURE_ALIAS,
247 TP_CONTACT_FEATURE_AVATAR_DATA,
248 TP_CONTACT_FEATURE_PRESENCE,
249 TP_CONTACT_FEATURE_CAPABILITIES };
251 conn = tp_account_get_connection (account);
255 /* One day we'll have tp_connection_get_account()... */
256 g_object_set_data_full (G_OBJECT (conn), "account",
257 g_object_ref (account), g_object_unref);
259 tp_connection_get_contacts_by_id (conn, 1, &id, G_N_ELEMENTS (features),
260 features, get_contacts_cb, self->priv->add_temp_ctx, NULL,
264 g_list_free (accounts);
268 search_text_changed (GtkEntry *entry,
269 EmpathyContactChooser *self)
273 tp_clear_pointer (&self->priv->search_words, g_ptr_array_unref);
274 tp_clear_pointer (&self->priv->search_str, g_free);
276 id = gtk_entry_get_text (entry);
278 self->priv->search_words = empathy_live_search_strip_utf8_string (id);
279 self->priv->search_str = g_strdup (id);
281 add_temporary_individuals (self, id);
283 empathy_individual_view_refilter (self->priv->view);
287 empathy_contact_chooser_init (EmpathyContactChooser *self)
289 EmpathyIndividualManager *mgr;
290 GtkTreeSelection *selection;
292 GtkWidget *search_entry;
293 GQuark features[] = { TP_ACCOUNT_MANAGER_FEATURE_CORE, 0 };
295 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EMPATHY_TYPE_CONTACT_CHOOSER,
296 EmpathyContactChooserPrivate);
298 self->priv->account_mgr = tp_account_manager_dup ();
300 /* We don't wait for the CORE feature to be prepared, which is fine as we
301 * won't use the account manager until user starts searching. Furthermore,
302 * the AM has probably already been prepared by another Empathy
304 tp_proxy_prepare_async (self->priv->account_mgr, features, NULL, NULL);
307 search_entry = gtk_entry_new ();
308 gtk_box_pack_start (GTK_BOX (self), search_entry, FALSE, TRUE, 6);
309 gtk_widget_show (search_entry);
311 g_signal_connect (search_entry, "changed",
312 G_CALLBACK (search_text_changed), self);
314 /* Add the treeview */
315 mgr = empathy_individual_manager_dup_singleton ();
316 self->priv->store = empathy_individual_store_new (mgr);
317 g_object_unref (mgr);
319 empathy_individual_store_set_show_groups (self->priv->store, FALSE);
321 self->priv->view = empathy_individual_view_new (self->priv->store,
322 EMPATHY_INDIVIDUAL_VIEW_FEATURE_NONE, EMPATHY_INDIVIDUAL_FEATURE_NONE);
324 empathy_individual_view_set_custom_filter (self->priv->view,
327 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->view));
329 g_signal_connect (selection, "changed",
330 G_CALLBACK (view_selection_changed_cb), self);
332 scroll = gtk_scrolled_window_new (NULL, NULL);
334 gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (self->priv->view));
336 gtk_box_pack_start (GTK_BOX (self), scroll, TRUE, TRUE, 6);
337 gtk_widget_show (GTK_WIDGET (self->priv->view));
338 gtk_widget_show (scroll);
342 empathy_contact_chooser_new (void)
344 return g_object_new (EMPATHY_TYPE_CONTACT_CHOOSER,
345 "orientation", GTK_ORIENTATION_VERTICAL,
350 empathy_contact_chooser_dup_selected (EmpathyContactChooser *self)
352 return empathy_individual_view_dup_selected (self->priv->view);
356 empathy_contact_chooser_set_filter_func (EmpathyContactChooser *self,
357 EmpathyContactChooserFilterFunc func,
360 g_assert (self->priv->filter_func == NULL);
362 self->priv->filter_func = func;
363 self->priv->filter_data = user_data;