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;
54 contact_chooser_get_property (GObject *object,
59 EmpathyContactChooser *self = (EmpathyContactChooser *)
65 g_value_set_object (value, self->priv->tp_chat);
68 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
74 contact_chooser_set_property (GObject *object,
79 EmpathyContactChooser *self = (EmpathyContactChooser *)
85 g_assert (self->priv->tp_chat == NULL); /* construct-only */
86 self->priv->tp_chat = g_value_dup_object (value);
89 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
94 struct _AddTemporaryIndividualCtx
96 EmpathyContactChooser *self;
97 /* List of owned FolksIndividual */
101 static AddTemporaryIndividualCtx *
102 add_temporary_individual_ctx_new (EmpathyContactChooser *self)
104 AddTemporaryIndividualCtx *ctx = g_slice_new0 (AddTemporaryIndividualCtx);
111 add_temporary_individual_ctx_free (AddTemporaryIndividualCtx *ctx)
115 /* Remove all the individuals from the model */
116 for (l = ctx->individuals; l != NULL; l = g_list_next (l))
118 FolksIndividual *individual = l->data;
120 individual_store_remove_individual_and_disconnect (ctx->self->priv->store,
123 g_object_unref (individual);
126 g_list_free (ctx->individuals);
127 g_slice_free (AddTemporaryIndividualCtx, ctx);
131 contact_chooser_dispose (GObject *object)
133 EmpathyContactChooser *self = (EmpathyContactChooser *)
136 tp_clear_pointer (&self->priv->add_temp_ctx,
137 add_temporary_individual_ctx_free);
139 tp_clear_object (&self->priv->tp_chat);
140 tp_clear_object (&self->priv->store);
141 tp_clear_pointer (&self->priv->search_words, g_ptr_array_unref);
142 tp_clear_pointer (&self->priv->search_str, g_free);
144 tp_clear_object (&self->priv->account_mgr);
146 G_OBJECT_CLASS (empathy_contact_chooser_parent_class)->dispose (
151 empathy_contact_chooser_class_init (
152 EmpathyContactChooserClass *klass)
154 GObjectClass *object_class = G_OBJECT_CLASS (klass);
156 object_class->get_property = contact_chooser_get_property;
157 object_class->set_property = contact_chooser_set_property;
158 object_class->dispose = contact_chooser_dispose;
160 g_type_class_add_private (object_class,
161 sizeof (EmpathyContactChooserPrivate));
163 g_object_class_install_property (object_class,
165 g_param_spec_object ("tp-chat", "EmpathyTpChat", "EmpathyTpChat",
166 EMPATHY_TYPE_TP_CHAT,
167 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
169 signals[SIG_SELECTION_CHANGED] = g_signal_new ("selection-changed",
170 G_TYPE_FROM_CLASS (klass),
174 g_cclosure_marshal_VOID__OBJECT,
176 1, FOLKS_TYPE_INDIVIDUAL);
180 view_selection_changed_cb (GtkWidget *treeview,
181 EmpathyContactChooser *self)
183 FolksIndividual *individual;
185 individual = empathy_individual_view_dup_selected (self->priv->view);
187 g_signal_emit (self, signals[SIG_SELECTION_CHANGED], 0, individual);
189 tp_clear_object (&individual);
192 /* Return the TpContact of @individual which is on the same connection as the
195 get_tp_contact_for_chat (EmpathyContactChooser *self,
196 FolksIndividual *individual)
198 TpContact *contact = NULL;
199 TpConnection *chat_conn;
203 chat_conn = tp_channel_borrow_connection (TP_CHANNEL (self->priv->tp_chat));
205 personas = folks_individual_get_personas (individual);
206 iter = gee_iterable_iterator (GEE_ITERABLE (personas));
207 while (contact == FALSE && gee_iterator_next (iter))
209 TpfPersona *persona = gee_iterator_get (iter);
210 TpConnection *contact_conn;
211 TpContact *contact_cur = NULL;
213 if (TPF_IS_PERSONA (persona))
215 contact_cur = tpf_persona_get_contact (persona);
216 if (contact_cur != NULL)
218 contact_conn = tp_contact_get_connection (contact_cur);
220 if (!tp_strdiff (tp_proxy_get_object_path (contact_conn),
221 tp_proxy_get_object_path (chat_conn)))
222 contact = contact_cur;
226 g_clear_object (&persona);
228 g_clear_object (&iter);
234 filter_func (GtkTreeModel *model,
238 EmpathyContactChooser *self = user_data;
239 FolksIndividual *individual;
243 gboolean display = FALSE;
245 gtk_tree_model_get (model, iter,
246 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual,
247 EMPATHY_INDIVIDUAL_STORE_COL_IS_ONLINE, &is_online,
250 if (individual == NULL)
253 if (self->priv->search_words == NULL)
255 /* Not searching, display online contacts */
261 if (!empathy_individual_match_string (individual,
262 self->priv->search_str, self->priv->search_words))
266 /* Filter out individuals not having a persona on the same connection as the
268 contact = get_tp_contact_for_chat (self, individual);
273 /* Filter out contacts which are already in the chat */
274 members = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (
275 self->priv->tp_chat));
279 for (l = members; l != NULL; l = g_list_next (l))
281 EmpathyContact *member = l->data;
284 /* Try to get the non-channel specific handle. */
285 handle = tp_channel_group_get_handle_owner (
286 TP_CHANNEL (self->priv->tp_chat),
287 empathy_contact_get_handle (member));
289 handle = empathy_contact_get_handle (member);
291 if (handle == tp_contact_get_handle (contact))
298 g_list_free_full (members, g_object_unref);
301 tp_clear_object (&individual);
306 get_contacts_cb (TpConnection *connection,
308 TpContact * const *contacts,
309 const gchar * const *requested_ids,
310 GHashTable *failed_id_errors,
313 GObject *weak_object)
315 EmpathyContactChooser *self =
316 (EmpathyContactChooser *) weak_object;
317 AddTemporaryIndividualCtx *ctx = user_data;
319 TpfPersonaStore *store;
320 FolksIndividual *individual;
321 TpfPersona *persona_new;
324 if (self->priv->add_temp_ctx != ctx)
325 /* another request has been started */
331 account = g_object_get_data (G_OBJECT (connection), "account");
333 store = tpf_persona_store_new (account);
335 gee_hash_set_new (FOLKS_TYPE_PERSONA, g_object_ref, g_object_unref,
336 g_direct_hash, g_direct_equal));
337 persona_new = tpf_persona_new (contacts[0], store);
338 gee_collection_add (GEE_COLLECTION (personas),
339 tpf_persona_new (contacts[0], store));
341 individual = folks_individual_new (personas);
343 /* Pass ownership to the list */
344 ctx->individuals = g_list_prepend (ctx->individuals, individual);
346 individual_store_add_individual_and_connect (self->priv->store, individual);
348 g_clear_object (&persona_new);
349 g_clear_object (&personas);
350 g_object_unref (store);
354 add_temporary_individuals (EmpathyContactChooser *self,
359 tp_clear_pointer (&self->priv->add_temp_ctx,
360 add_temporary_individual_ctx_free);
362 if (tp_str_empty (id))
365 self->priv->add_temp_ctx = add_temporary_individual_ctx_new (self);
367 /* Try to add an individual for each connected account */
368 accounts = tp_account_manager_get_valid_accounts (self->priv->account_mgr);
369 for (l = accounts; l != NULL; l = g_list_next (l))
371 TpAccount *account = l->data;
373 TpContactFeature features[] = { TP_CONTACT_FEATURE_ALIAS,
374 TP_CONTACT_FEATURE_AVATAR_DATA,
375 TP_CONTACT_FEATURE_PRESENCE,
376 TP_CONTACT_FEATURE_CAPABILITIES };
378 conn = tp_account_get_connection (account);
382 /* One day we'll have tp_connection_get_account()... */
383 g_object_set_data_full (G_OBJECT (conn), "account",
384 g_object_ref (account), g_object_unref);
386 tp_connection_get_contacts_by_id (conn, 1, &id, G_N_ELEMENTS (features),
387 features, get_contacts_cb, self->priv->add_temp_ctx, NULL,
391 g_list_free (accounts);
395 search_text_changed (GtkEntry *entry,
396 EmpathyContactChooser *self)
400 tp_clear_pointer (&self->priv->search_words, g_ptr_array_unref);
401 tp_clear_pointer (&self->priv->search_str, g_free);
403 id = gtk_entry_get_text (entry);
405 self->priv->search_words = empathy_live_search_strip_utf8_string (id);
406 self->priv->search_str = g_strdup (id);
408 add_temporary_individuals (self, id);
410 empathy_individual_view_refilter (self->priv->view);
414 empathy_contact_chooser_init (EmpathyContactChooser *self)
416 EmpathyIndividualManager *mgr;
417 GtkTreeSelection *selection;
419 GtkWidget *search_entry;
420 GQuark features[] = { TP_ACCOUNT_MANAGER_FEATURE_CORE, 0 };
422 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EMPATHY_TYPE_CONTACT_CHOOSER,
423 EmpathyContactChooserPrivate);
425 self->priv->account_mgr = tp_account_manager_dup ();
427 /* We don't wait for the CORE feature to be prepared, which is fine as we
428 * won't use the account manager until user starts searching. Furthermore,
429 * the AM has probably already been prepared by another Empathy
431 tp_proxy_prepare_async (self->priv->account_mgr, features, NULL, NULL);
434 search_entry = gtk_entry_new ();
435 gtk_box_pack_start (GTK_BOX (self), search_entry, FALSE, TRUE, 6);
436 gtk_widget_show (search_entry);
438 g_signal_connect (search_entry, "changed",
439 G_CALLBACK (search_text_changed), self);
441 /* Add the treeview */
442 mgr = empathy_individual_manager_dup_singleton ();
443 self->priv->store = empathy_individual_store_new (mgr);
444 g_object_unref (mgr);
446 empathy_individual_store_set_show_groups (self->priv->store, FALSE);
448 self->priv->view = empathy_individual_view_new (self->priv->store,
449 EMPATHY_INDIVIDUAL_VIEW_FEATURE_NONE, EMPATHY_INDIVIDUAL_FEATURE_NONE);
451 empathy_individual_view_set_custom_filter (self->priv->view,
454 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->view));
456 g_signal_connect (selection, "changed",
457 G_CALLBACK (view_selection_changed_cb), self);
459 scroll = gtk_scrolled_window_new (NULL, NULL);
461 gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (self->priv->view));
463 gtk_box_pack_start (GTK_BOX (self), scroll, TRUE, TRUE, 6);
464 gtk_widget_show (GTK_WIDGET (self->priv->view));
465 gtk_widget_show (scroll);
469 empathy_contact_chooser_new (EmpathyTpChat *tp_chat)
471 g_return_val_if_fail (EMPATHY_IS_TP_CHAT (tp_chat), NULL);
473 return g_object_new (EMPATHY_TYPE_CONTACT_CHOOSER,
474 "orientation", GTK_ORIENTATION_VERTICAL,
480 empathy_contact_chooser_get_selected (EmpathyContactChooser *self)
482 FolksIndividual *individual;
485 individual = empathy_individual_view_dup_selected (self->priv->view);
486 if (individual == NULL)
489 contact = get_tp_contact_for_chat (self, individual);
491 g_object_unref (individual);