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);
195 /* Return the TpContact of @individual which is on the same connection as the
198 get_tp_contact_for_chat (EmpathyContactChooser *self,
199 FolksIndividual *individual)
201 TpContact *contact = NULL;
202 TpConnection *chat_conn;
206 chat_conn = tp_channel_borrow_connection (TP_CHANNEL (self->priv->tp_chat));
208 personas = folks_individual_get_personas (individual);
209 iter = gee_iterable_iterator (GEE_ITERABLE (personas));
210 while (contact == FALSE && gee_iterator_next (iter))
212 TpfPersona *persona = gee_iterator_get (iter);
213 TpConnection *contact_conn;
214 TpContact *contact_cur = NULL;
216 if (TPF_IS_PERSONA (persona))
218 contact_cur = tpf_persona_get_contact (persona);
219 if (contact_cur != NULL)
221 contact_conn = tp_contact_get_connection (contact_cur);
223 if (!tp_strdiff (tp_proxy_get_object_path (contact_conn),
224 tp_proxy_get_object_path (chat_conn)))
225 contact = contact_cur;
229 g_clear_object (&persona);
231 g_clear_object (&iter);
237 filter_func (GtkTreeModel *model,
241 EmpathyContactChooser *self = user_data;
242 FolksIndividual *individual;
244 gboolean display = FALSE;
245 gboolean searching = FALSE;
247 gtk_tree_model_get (model, iter,
248 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual,
249 EMPATHY_INDIVIDUAL_STORE_COL_IS_ONLINE, &is_online,
252 if (individual == NULL)
255 if (self->priv->search_words != NULL)
259 /* Filter out the contact if we are searching and it doesn't match */
260 if (!empathy_individual_match_string (individual,
261 self->priv->search_str, self->priv->search_words))
265 if (self->priv->filter_func == NULL)
268 display = self->priv->filter_func (self, individual, is_online, searching,
269 self->priv->filter_data);
271 tp_clear_object (&individual);
276 get_contacts_cb (TpConnection *connection,
278 TpContact * const *contacts,
279 const gchar * const *requested_ids,
280 GHashTable *failed_id_errors,
283 GObject *weak_object)
285 EmpathyContactChooser *self =
286 (EmpathyContactChooser *) weak_object;
287 AddTemporaryIndividualCtx *ctx = user_data;
289 TpfPersonaStore *store;
290 FolksIndividual *individual;
291 TpfPersona *persona_new;
294 if (self->priv->add_temp_ctx != ctx)
295 /* another request has been started */
301 account = g_object_get_data (G_OBJECT (connection), "account");
303 store = tpf_persona_store_new (account);
305 gee_hash_set_new (FOLKS_TYPE_PERSONA, g_object_ref, g_object_unref,
306 g_direct_hash, g_direct_equal));
307 persona_new = tpf_persona_new (contacts[0], store);
308 gee_collection_add (GEE_COLLECTION (personas),
309 tpf_persona_new (contacts[0], store));
311 individual = folks_individual_new (personas);
313 /* Pass ownership to the list */
314 ctx->individuals = g_list_prepend (ctx->individuals, individual);
316 individual_store_add_individual_and_connect (self->priv->store, individual);
318 g_clear_object (&persona_new);
319 g_clear_object (&personas);
320 g_object_unref (store);
324 add_temporary_individuals (EmpathyContactChooser *self,
329 tp_clear_pointer (&self->priv->add_temp_ctx,
330 add_temporary_individual_ctx_free);
332 if (tp_str_empty (id))
335 self->priv->add_temp_ctx = add_temporary_individual_ctx_new (self);
337 /* Try to add an individual for each connected account */
338 accounts = tp_account_manager_get_valid_accounts (self->priv->account_mgr);
339 for (l = accounts; l != NULL; l = g_list_next (l))
341 TpAccount *account = l->data;
343 TpContactFeature features[] = { TP_CONTACT_FEATURE_ALIAS,
344 TP_CONTACT_FEATURE_AVATAR_DATA,
345 TP_CONTACT_FEATURE_PRESENCE,
346 TP_CONTACT_FEATURE_CAPABILITIES };
348 conn = tp_account_get_connection (account);
352 /* One day we'll have tp_connection_get_account()... */
353 g_object_set_data_full (G_OBJECT (conn), "account",
354 g_object_ref (account), g_object_unref);
356 tp_connection_get_contacts_by_id (conn, 1, &id, G_N_ELEMENTS (features),
357 features, get_contacts_cb, self->priv->add_temp_ctx, NULL,
361 g_list_free (accounts);
365 search_text_changed (GtkEntry *entry,
366 EmpathyContactChooser *self)
370 tp_clear_pointer (&self->priv->search_words, g_ptr_array_unref);
371 tp_clear_pointer (&self->priv->search_str, g_free);
373 id = gtk_entry_get_text (entry);
375 self->priv->search_words = empathy_live_search_strip_utf8_string (id);
376 self->priv->search_str = g_strdup (id);
378 add_temporary_individuals (self, id);
380 empathy_individual_view_refilter (self->priv->view);
384 empathy_contact_chooser_init (EmpathyContactChooser *self)
386 EmpathyIndividualManager *mgr;
387 GtkTreeSelection *selection;
389 GtkWidget *search_entry;
390 GQuark features[] = { TP_ACCOUNT_MANAGER_FEATURE_CORE, 0 };
392 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EMPATHY_TYPE_CONTACT_CHOOSER,
393 EmpathyContactChooserPrivate);
395 self->priv->account_mgr = tp_account_manager_dup ();
397 /* We don't wait for the CORE feature to be prepared, which is fine as we
398 * won't use the account manager until user starts searching. Furthermore,
399 * the AM has probably already been prepared by another Empathy
401 tp_proxy_prepare_async (self->priv->account_mgr, features, NULL, NULL);
404 search_entry = gtk_entry_new ();
405 gtk_box_pack_start (GTK_BOX (self), search_entry, FALSE, TRUE, 6);
406 gtk_widget_show (search_entry);
408 g_signal_connect (search_entry, "changed",
409 G_CALLBACK (search_text_changed), self);
411 /* Add the treeview */
412 mgr = empathy_individual_manager_dup_singleton ();
413 self->priv->store = empathy_individual_store_new (mgr);
414 g_object_unref (mgr);
416 empathy_individual_store_set_show_groups (self->priv->store, FALSE);
418 self->priv->view = empathy_individual_view_new (self->priv->store,
419 EMPATHY_INDIVIDUAL_VIEW_FEATURE_NONE, EMPATHY_INDIVIDUAL_FEATURE_NONE);
421 empathy_individual_view_set_custom_filter (self->priv->view,
424 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->view));
426 g_signal_connect (selection, "changed",
427 G_CALLBACK (view_selection_changed_cb), self);
429 scroll = gtk_scrolled_window_new (NULL, NULL);
431 gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (self->priv->view));
433 gtk_box_pack_start (GTK_BOX (self), scroll, TRUE, TRUE, 6);
434 gtk_widget_show (GTK_WIDGET (self->priv->view));
435 gtk_widget_show (scroll);
439 empathy_contact_chooser_new (EmpathyTpChat *tp_chat)
441 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (tp_chat), NULL);
443 return g_object_new (EMPATHY_TYPE_CONTACT_CHOOSER,
444 "orientation", GTK_ORIENTATION_VERTICAL,
450 empathy_contact_chooser_get_selected (EmpathyContactChooser *self)
452 FolksIndividual *individual;
455 individual = empathy_individual_view_dup_selected (self->priv->view);
456 if (individual == NULL)
459 contact = get_tp_contact_for_chat (self, individual);
461 g_object_unref (individual);
466 empathy_contact_chooser_set_filter_func (EmpathyContactChooser *self,
467 EmpathyContactChooserFilterFunc func,
470 g_assert (self->priv->filter_func == NULL);
472 self->priv->filter_func = func;
473 self->priv->filter_data = user_data;