]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-contact-chooser.c
contact-chooser: use tp_connection_get_account()
[empathy.git] / libempathy-gtk / empathy-contact-chooser.c
1 /*
2  * empathy-contact-chooser.c
3  *
4  * EmpathyContactChooser
5  *
6  * (c) 2009, Collabora Ltd.
7  *
8  * Authors:
9  *    Danielle Madeley <danielle.madeley@collabora.co.uk>
10  */
11
12 #include <glib/gi18n.h>
13 #include <folks/folks-telepathy.h>
14
15 #include "empathy-contact-chooser.h"
16
17 #include <libempathy-gtk/empathy-individual-view.h>
18 #include <libempathy-gtk/empathy-ui-utils.h>
19
20 G_DEFINE_TYPE (EmpathyContactChooser,
21     empathy_contact_chooser, GTK_TYPE_BOX);
22
23 enum {
24   SIG_SELECTION_CHANGED,
25   LAST_SIGNAL
26 };
27
28 static guint signals[LAST_SIGNAL];
29
30 typedef struct _AddTemporaryIndividualCtx AddTemporaryIndividualCtx;
31
32 struct _EmpathyContactChooserPrivate
33 {
34   TpAccountManager *account_mgr;
35
36   EmpathyIndividualStore *store;
37   EmpathyIndividualView *view;
38
39   GPtrArray *search_words;
40   gchar *search_str;
41
42   /* Context representing the FolksIndividual which are added because of the
43    * current search from the user. */
44   AddTemporaryIndividualCtx *add_temp_ctx;
45
46   EmpathyContactChooserFilterFunc filter_func;
47   gpointer filter_data;
48 };
49
50 struct _AddTemporaryIndividualCtx
51 {
52   EmpathyContactChooser *self;
53   /* List of owned FolksIndividual */
54   GList *individuals;
55 };
56
57 static AddTemporaryIndividualCtx *
58 add_temporary_individual_ctx_new (EmpathyContactChooser *self)
59 {
60   AddTemporaryIndividualCtx *ctx = g_slice_new0 (AddTemporaryIndividualCtx);
61
62   ctx->self = self;
63   return ctx;
64 }
65
66 static void
67 add_temporary_individual_ctx_free (AddTemporaryIndividualCtx *ctx)
68 {
69   GList *l;
70
71   /* Remove all the individuals from the model */
72   for (l = ctx->individuals; l != NULL; l = g_list_next (l))
73     {
74       FolksIndividual *individual = l->data;
75
76       individual_store_remove_individual_and_disconnect (ctx->self->priv->store,
77           individual);
78
79       g_object_unref (individual);
80     }
81
82   g_list_free (ctx->individuals);
83   g_slice_free (AddTemporaryIndividualCtx, ctx);
84 }
85
86 static void
87 contact_chooser_dispose (GObject *object)
88 {
89   EmpathyContactChooser *self = (EmpathyContactChooser *)
90     object;
91
92   tp_clear_pointer (&self->priv->add_temp_ctx,
93       add_temporary_individual_ctx_free);
94
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);
98
99   tp_clear_object (&self->priv->account_mgr);
100
101   G_OBJECT_CLASS (empathy_contact_chooser_parent_class)->dispose (
102       object);
103 }
104
105 static void
106 empathy_contact_chooser_class_init (
107     EmpathyContactChooserClass *klass)
108 {
109   GObjectClass *object_class = G_OBJECT_CLASS (klass);
110
111   object_class->dispose = contact_chooser_dispose;
112
113   g_type_class_add_private (object_class,
114       sizeof (EmpathyContactChooserPrivate));
115
116   signals[SIG_SELECTION_CHANGED] = g_signal_new ("selection-changed",
117             G_TYPE_FROM_CLASS (klass),
118             G_SIGNAL_RUN_LAST,
119             0,
120             NULL, NULL,
121             g_cclosure_marshal_VOID__OBJECT,
122             G_TYPE_NONE,
123             1, FOLKS_TYPE_INDIVIDUAL);
124 }
125
126 static void
127 view_selection_changed_cb (GtkWidget *treeview,
128     EmpathyContactChooser *self)
129 {
130   FolksIndividual *individual;
131
132   individual = empathy_individual_view_dup_selected (self->priv->view);
133
134   g_signal_emit (self, signals[SIG_SELECTION_CHANGED], 0, individual);
135
136   tp_clear_object (&individual);
137 }
138
139 static gboolean
140 filter_func (GtkTreeModel *model,
141     GtkTreeIter *iter,
142     gpointer user_data)
143 {
144   EmpathyContactChooser *self = user_data;
145   FolksIndividual *individual;
146   gboolean is_online;
147   gboolean display = FALSE;
148   gboolean searching = FALSE;
149
150   gtk_tree_model_get (model, iter,
151       EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual,
152       EMPATHY_INDIVIDUAL_STORE_COL_IS_ONLINE, &is_online,
153       -1);
154
155   if (individual == NULL)
156     goto out;
157
158   if (self->priv->search_words != NULL)
159     {
160       searching = TRUE;
161
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))
165         goto out;
166     }
167
168   if (self->priv->filter_func == NULL)
169     display = TRUE;
170   else
171     display = self->priv->filter_func (self, individual, is_online, searching,
172       self->priv->filter_data);
173 out:
174   tp_clear_object (&individual);
175   return display;
176 }
177
178 static void
179 get_contacts_cb (TpConnection *connection,
180     guint n_contacts,
181     TpContact * const *contacts,
182     const gchar * const *requested_ids,
183     GHashTable *failed_id_errors,
184     const GError *error,
185     gpointer user_data,
186     GObject *weak_object)
187 {
188   EmpathyContactChooser *self =
189     (EmpathyContactChooser *) weak_object;
190   AddTemporaryIndividualCtx *ctx = user_data;
191   TpAccount *account;
192   TpfPersonaStore *store;
193   FolksIndividual *individual;
194   TpfPersona *persona_new;
195   GeeSet *personas;
196
197   if (self->priv->add_temp_ctx != ctx)
198     /* another request has been started */
199     return;
200
201   if (n_contacts != 1)
202     return;
203
204   account = tp_connection_get_account (connection);
205
206   store = tpf_persona_store_new (account);
207   personas = GEE_SET (
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));
213
214   individual = folks_individual_new (personas);
215
216   /* Pass ownership to the list */
217   ctx->individuals = g_list_prepend (ctx->individuals, individual);
218
219   individual_store_add_individual_and_connect (self->priv->store, individual);
220
221   g_clear_object (&persona_new);
222   g_clear_object (&personas);
223   g_object_unref (store);
224 }
225
226 static void
227 add_temporary_individuals (EmpathyContactChooser *self,
228     const gchar *id)
229 {
230   GList *accounts, *l;
231
232   tp_clear_pointer (&self->priv->add_temp_ctx,
233       add_temporary_individual_ctx_free);
234
235   if (tp_str_empty (id))
236     return;
237
238   self->priv->add_temp_ctx = add_temporary_individual_ctx_new (self);
239
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))
243     {
244       TpAccount *account = l->data;
245       TpConnection *conn;
246       TpContactFeature features[] = { TP_CONTACT_FEATURE_ALIAS,
247           TP_CONTACT_FEATURE_AVATAR_DATA,
248           TP_CONTACT_FEATURE_PRESENCE,
249           TP_CONTACT_FEATURE_CAPABILITIES };
250
251       conn = tp_account_get_connection (account);
252       if (conn == NULL)
253         continue;
254
255       tp_connection_get_contacts_by_id (conn, 1, &id, G_N_ELEMENTS (features),
256           features, get_contacts_cb, self->priv->add_temp_ctx, NULL,
257           G_OBJECT (self));
258     }
259
260   g_list_free (accounts);
261 }
262
263 static void
264 search_text_changed (GtkEntry *entry,
265     EmpathyContactChooser *self)
266 {
267   const gchar *id;
268
269   tp_clear_pointer (&self->priv->search_words, g_ptr_array_unref);
270   tp_clear_pointer (&self->priv->search_str, g_free);
271
272   id = gtk_entry_get_text (entry);
273
274   self->priv->search_words = empathy_live_search_strip_utf8_string (id);
275   self->priv->search_str = g_strdup (id);
276
277   add_temporary_individuals (self, id);
278
279   empathy_individual_view_refilter (self->priv->view);
280 }
281
282 static void
283 empathy_contact_chooser_init (EmpathyContactChooser *self)
284 {
285   EmpathyIndividualManager *mgr;
286   GtkTreeSelection *selection;
287   GtkWidget *scroll;
288   GtkWidget *search_entry;
289   GQuark features[] = { TP_ACCOUNT_MANAGER_FEATURE_CORE, 0 };
290
291   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EMPATHY_TYPE_CONTACT_CHOOSER,
292       EmpathyContactChooserPrivate);
293
294   self->priv->account_mgr = tp_account_manager_dup ();
295
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
299    * component. */
300   tp_proxy_prepare_async (self->priv->account_mgr, features, NULL, NULL);
301
302   /* Search entry */
303   search_entry = gtk_entry_new ();
304   gtk_box_pack_start (GTK_BOX (self), search_entry, FALSE, TRUE, 6);
305   gtk_widget_show (search_entry);
306
307   g_signal_connect (search_entry, "changed",
308       G_CALLBACK (search_text_changed), self);
309
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);
314
315   empathy_individual_store_set_show_groups (self->priv->store, FALSE);
316
317   self->priv->view = empathy_individual_view_new (self->priv->store,
318       EMPATHY_INDIVIDUAL_VIEW_FEATURE_NONE, EMPATHY_INDIVIDUAL_FEATURE_NONE);
319
320   empathy_individual_view_set_custom_filter (self->priv->view,
321       filter_func, self);
322
323   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->view));
324
325   g_signal_connect (selection, "changed",
326       G_CALLBACK (view_selection_changed_cb), self);
327
328   scroll = gtk_scrolled_window_new (NULL, NULL);
329
330   gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (self->priv->view));
331
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);
335 }
336
337 GtkWidget *
338 empathy_contact_chooser_new (void)
339 {
340   return g_object_new (EMPATHY_TYPE_CONTACT_CHOOSER,
341       "orientation", GTK_ORIENTATION_VERTICAL,
342       NULL);
343 }
344
345 FolksIndividual *
346 empathy_contact_chooser_dup_selected (EmpathyContactChooser *self)
347 {
348   return empathy_individual_view_dup_selected (self->priv->view);
349 }
350
351 void
352 empathy_contact_chooser_set_filter_func (EmpathyContactChooser *self,
353     EmpathyContactChooserFilterFunc func,
354     gpointer user_data)
355 {
356   g_assert (self->priv->filter_func == NULL);
357
358   self->priv->filter_func = func;
359   self->priv->filter_data = user_data;
360 }