]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-contact-chooser.c
EmpathyContactChooser: remove EmpathyTpChat property
[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 = g_object_get_data (G_OBJECT (connection), "account");
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       /* 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);
258
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,
261           G_OBJECT (self));
262     }
263
264   g_list_free (accounts);
265 }
266
267 static void
268 search_text_changed (GtkEntry *entry,
269     EmpathyContactChooser *self)
270 {
271   const gchar *id;
272
273   tp_clear_pointer (&self->priv->search_words, g_ptr_array_unref);
274   tp_clear_pointer (&self->priv->search_str, g_free);
275
276   id = gtk_entry_get_text (entry);
277
278   self->priv->search_words = empathy_live_search_strip_utf8_string (id);
279   self->priv->search_str = g_strdup (id);
280
281   add_temporary_individuals (self, id);
282
283   empathy_individual_view_refilter (self->priv->view);
284 }
285
286 static void
287 empathy_contact_chooser_init (EmpathyContactChooser *self)
288 {
289   EmpathyIndividualManager *mgr;
290   GtkTreeSelection *selection;
291   GtkWidget *scroll;
292   GtkWidget *search_entry;
293   GQuark features[] = { TP_ACCOUNT_MANAGER_FEATURE_CORE, 0 };
294
295   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EMPATHY_TYPE_CONTACT_CHOOSER,
296       EmpathyContactChooserPrivate);
297
298   self->priv->account_mgr = tp_account_manager_dup ();
299
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
303    * component. */
304   tp_proxy_prepare_async (self->priv->account_mgr, features, NULL, NULL);
305
306   /* Search entry */
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);
310
311   g_signal_connect (search_entry, "changed",
312       G_CALLBACK (search_text_changed), self);
313
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);
318
319   empathy_individual_store_set_show_groups (self->priv->store, FALSE);
320
321   self->priv->view = empathy_individual_view_new (self->priv->store,
322       EMPATHY_INDIVIDUAL_VIEW_FEATURE_NONE, EMPATHY_INDIVIDUAL_FEATURE_NONE);
323
324   empathy_individual_view_set_custom_filter (self->priv->view,
325       filter_func, self);
326
327   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->view));
328
329   g_signal_connect (selection, "changed",
330       G_CALLBACK (view_selection_changed_cb), self);
331
332   scroll = gtk_scrolled_window_new (NULL, NULL);
333
334   gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (self->priv->view));
335
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);
339 }
340
341 GtkWidget *
342 empathy_contact_chooser_new (void)
343 {
344   return g_object_new (EMPATHY_TYPE_CONTACT_CHOOSER,
345       "orientation", GTK_ORIENTATION_VERTICAL,
346       NULL);
347 }
348
349 FolksIndividual *
350 empathy_contact_chooser_dup_selected (EmpathyContactChooser *self)
351 {
352   return empathy_individual_view_dup_selected (self->priv->view);
353 }
354
355 void
356 empathy_contact_chooser_set_filter_func (EmpathyContactChooser *self,
357     EmpathyContactChooserFilterFunc func,
358     gpointer user_data)
359 {
360   g_assert (self->priv->filter_func == NULL);
361
362   self->priv->filter_func = func;
363   self->priv->filter_data = user_data;
364 }