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);
29 SIG_SELECTION_CHANGED,
33 static guint signals[LAST_SIGNAL];
35 typedef struct _AddTemporaryIndividualCtx AddTemporaryIndividualCtx;
37 struct _EmpathyContactChooserPrivate
39 EmpathyTpChat *tp_chat;
40 TpAccountManager *account_mgr;
42 EmpathyIndividualStore *store;
43 EmpathyIndividualView *view;
45 GPtrArray *search_words;
48 /* Context representing the FolksIndividual which are added because of the
49 * current search from the user. */
50 AddTemporaryIndividualCtx *add_temp_ctx;
52 EmpathyContactChooserFilterFunc filter_func;
57 contact_chooser_get_property (GObject *object,
62 EmpathyContactChooser *self = (EmpathyContactChooser *)
68 g_value_set_object (value, self->priv->tp_chat);
71 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
77 contact_chooser_set_property (GObject *object,
82 EmpathyContactChooser *self = (EmpathyContactChooser *)
88 g_assert (self->priv->tp_chat == NULL); /* construct-only */
89 self->priv->tp_chat = g_value_dup_object (value);
92 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
97 struct _AddTemporaryIndividualCtx
99 EmpathyContactChooser *self;
100 /* List of owned FolksIndividual */
104 static AddTemporaryIndividualCtx *
105 add_temporary_individual_ctx_new (EmpathyContactChooser *self)
107 AddTemporaryIndividualCtx *ctx = g_slice_new0 (AddTemporaryIndividualCtx);
114 add_temporary_individual_ctx_free (AddTemporaryIndividualCtx *ctx)
118 /* Remove all the individuals from the model */
119 for (l = ctx->individuals; l != NULL; l = g_list_next (l))
121 FolksIndividual *individual = l->data;
123 individual_store_remove_individual_and_disconnect (ctx->self->priv->store,
126 g_object_unref (individual);
129 g_list_free (ctx->individuals);
130 g_slice_free (AddTemporaryIndividualCtx, ctx);
134 contact_chooser_dispose (GObject *object)
136 EmpathyContactChooser *self = (EmpathyContactChooser *)
139 tp_clear_pointer (&self->priv->add_temp_ctx,
140 add_temporary_individual_ctx_free);
142 tp_clear_object (&self->priv->tp_chat);
143 tp_clear_object (&self->priv->store);
144 tp_clear_pointer (&self->priv->search_words, g_ptr_array_unref);
145 tp_clear_pointer (&self->priv->search_str, g_free);
147 tp_clear_object (&self->priv->account_mgr);
149 G_OBJECT_CLASS (empathy_contact_chooser_parent_class)->dispose (
154 empathy_contact_chooser_class_init (
155 EmpathyContactChooserClass *klass)
157 GObjectClass *object_class = G_OBJECT_CLASS (klass);
159 object_class->get_property = contact_chooser_get_property;
160 object_class->set_property = contact_chooser_set_property;
161 object_class->dispose = contact_chooser_dispose;
163 g_type_class_add_private (object_class,
164 sizeof (EmpathyContactChooserPrivate));
166 g_object_class_install_property (object_class,
168 g_param_spec_object ("tp-chat", "EmpathyTpChat", "EmpathyTpChat",
169 EMPATHY_TYPE_TP_CHAT,
170 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
172 signals[SIG_SELECTION_CHANGED] = g_signal_new ("selection-changed",
173 G_TYPE_FROM_CLASS (klass),
177 g_cclosure_marshal_VOID__OBJECT,
179 1, FOLKS_TYPE_INDIVIDUAL);
183 view_selection_changed_cb (GtkWidget *treeview,
184 EmpathyContactChooser *self)
186 FolksIndividual *individual;
188 individual = empathy_individual_view_dup_selected (self->priv->view);
190 g_signal_emit (self, signals[SIG_SELECTION_CHANGED], 0, individual);
192 tp_clear_object (&individual);
196 filter_func (GtkTreeModel *model,
200 EmpathyContactChooser *self = user_data;
201 FolksIndividual *individual;
203 gboolean display = FALSE;
204 gboolean searching = FALSE;
206 gtk_tree_model_get (model, iter,
207 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual,
208 EMPATHY_INDIVIDUAL_STORE_COL_IS_ONLINE, &is_online,
211 if (individual == NULL)
214 if (self->priv->search_words != NULL)
218 /* Filter out the contact if we are searching and it doesn't match */
219 if (!empathy_individual_match_string (individual,
220 self->priv->search_str, self->priv->search_words))
224 if (self->priv->filter_func == NULL)
227 display = self->priv->filter_func (self, individual, is_online, searching,
228 self->priv->filter_data);
230 tp_clear_object (&individual);
235 get_contacts_cb (TpConnection *connection,
237 TpContact * const *contacts,
238 const gchar * const *requested_ids,
239 GHashTable *failed_id_errors,
242 GObject *weak_object)
244 EmpathyContactChooser *self =
245 (EmpathyContactChooser *) weak_object;
246 AddTemporaryIndividualCtx *ctx = user_data;
248 TpfPersonaStore *store;
249 FolksIndividual *individual;
250 TpfPersona *persona_new;
253 if (self->priv->add_temp_ctx != ctx)
254 /* another request has been started */
260 account = g_object_get_data (G_OBJECT (connection), "account");
262 store = tpf_persona_store_new (account);
264 gee_hash_set_new (FOLKS_TYPE_PERSONA, g_object_ref, g_object_unref,
265 g_direct_hash, g_direct_equal));
266 persona_new = tpf_persona_new (contacts[0], store);
267 gee_collection_add (GEE_COLLECTION (personas),
268 tpf_persona_new (contacts[0], store));
270 individual = folks_individual_new (personas);
272 /* Pass ownership to the list */
273 ctx->individuals = g_list_prepend (ctx->individuals, individual);
275 individual_store_add_individual_and_connect (self->priv->store, individual);
277 g_clear_object (&persona_new);
278 g_clear_object (&personas);
279 g_object_unref (store);
283 add_temporary_individuals (EmpathyContactChooser *self,
288 tp_clear_pointer (&self->priv->add_temp_ctx,
289 add_temporary_individual_ctx_free);
291 if (tp_str_empty (id))
294 self->priv->add_temp_ctx = add_temporary_individual_ctx_new (self);
296 /* Try to add an individual for each connected account */
297 accounts = tp_account_manager_get_valid_accounts (self->priv->account_mgr);
298 for (l = accounts; l != NULL; l = g_list_next (l))
300 TpAccount *account = l->data;
302 TpContactFeature features[] = { TP_CONTACT_FEATURE_ALIAS,
303 TP_CONTACT_FEATURE_AVATAR_DATA,
304 TP_CONTACT_FEATURE_PRESENCE,
305 TP_CONTACT_FEATURE_CAPABILITIES };
307 conn = tp_account_get_connection (account);
311 /* One day we'll have tp_connection_get_account()... */
312 g_object_set_data_full (G_OBJECT (conn), "account",
313 g_object_ref (account), g_object_unref);
315 tp_connection_get_contacts_by_id (conn, 1, &id, G_N_ELEMENTS (features),
316 features, get_contacts_cb, self->priv->add_temp_ctx, NULL,
320 g_list_free (accounts);
324 search_text_changed (GtkEntry *entry,
325 EmpathyContactChooser *self)
329 tp_clear_pointer (&self->priv->search_words, g_ptr_array_unref);
330 tp_clear_pointer (&self->priv->search_str, g_free);
332 id = gtk_entry_get_text (entry);
334 self->priv->search_words = empathy_live_search_strip_utf8_string (id);
335 self->priv->search_str = g_strdup (id);
337 add_temporary_individuals (self, id);
339 empathy_individual_view_refilter (self->priv->view);
343 empathy_contact_chooser_init (EmpathyContactChooser *self)
345 EmpathyIndividualManager *mgr;
346 GtkTreeSelection *selection;
348 GtkWidget *search_entry;
349 GQuark features[] = { TP_ACCOUNT_MANAGER_FEATURE_CORE, 0 };
351 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EMPATHY_TYPE_CONTACT_CHOOSER,
352 EmpathyContactChooserPrivate);
354 self->priv->account_mgr = tp_account_manager_dup ();
356 /* We don't wait for the CORE feature to be prepared, which is fine as we
357 * won't use the account manager until user starts searching. Furthermore,
358 * the AM has probably already been prepared by another Empathy
360 tp_proxy_prepare_async (self->priv->account_mgr, features, NULL, NULL);
363 search_entry = gtk_entry_new ();
364 gtk_box_pack_start (GTK_BOX (self), search_entry, FALSE, TRUE, 6);
365 gtk_widget_show (search_entry);
367 g_signal_connect (search_entry, "changed",
368 G_CALLBACK (search_text_changed), self);
370 /* Add the treeview */
371 mgr = empathy_individual_manager_dup_singleton ();
372 self->priv->store = empathy_individual_store_new (mgr);
373 g_object_unref (mgr);
375 empathy_individual_store_set_show_groups (self->priv->store, FALSE);
377 self->priv->view = empathy_individual_view_new (self->priv->store,
378 EMPATHY_INDIVIDUAL_VIEW_FEATURE_NONE, EMPATHY_INDIVIDUAL_FEATURE_NONE);
380 empathy_individual_view_set_custom_filter (self->priv->view,
383 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->view));
385 g_signal_connect (selection, "changed",
386 G_CALLBACK (view_selection_changed_cb), self);
388 scroll = gtk_scrolled_window_new (NULL, NULL);
390 gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (self->priv->view));
392 gtk_box_pack_start (GTK_BOX (self), scroll, TRUE, TRUE, 6);
393 gtk_widget_show (GTK_WIDGET (self->priv->view));
394 gtk_widget_show (scroll);
398 empathy_contact_chooser_new (EmpathyTpChat *tp_chat)
400 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (tp_chat), NULL);
402 return g_object_new (EMPATHY_TYPE_CONTACT_CHOOSER,
403 "orientation", GTK_ORIENTATION_VERTICAL,
409 empathy_contact_chooser_dup_selected (EmpathyContactChooser *self)
411 return empathy_individual_view_dup_selected (self->priv->view);
415 empathy_contact_chooser_set_filter_func (EmpathyContactChooser *self,
416 EmpathyContactChooserFilterFunc func,
419 g_assert (self->priv->filter_func == NULL);
421 self->priv->filter_func = func;
422 self->priv->filter_data = user_data;