]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-contact-selector-dialog.c
[EmpathyContactSelectorDialog] normalise case in match function
[empathy.git] / libempathy-gtk / empathy-contact-selector-dialog.c
1 /*
2  * Copyright (C) 2007-2009 Collabora Ltd.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  * Authors: Xavier Claessens <xclaesse@gmail.com>
19  * Authors: Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
20  * Authors: Danielle Madeley <danielle.madeley@collabora.co.uk>
21  */
22
23 #include <config.h>
24
25 #include <string.h>
26 #include <stdlib.h>
27
28 #include <gtk/gtk.h>
29 #include <glib/gi18n-lib.h>
30
31 #include <libempathy/empathy-tp-contact-factory.h>
32 #include <libempathy/empathy-contact-manager.h>
33 #include <libempathy/empathy-dispatcher.h>
34 #include <libempathy/empathy-utils.h>
35
36 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
37 #include <libempathy/empathy-debug.h>
38
39 #include <libempathy-gtk/empathy-ui-utils.h>
40 #include <libempathy-gtk/empathy-images.h>
41
42 #include "empathy-contact-selector-dialog.h"
43 #include "empathy-account-chooser.h"
44
45 G_DEFINE_ABSTRACT_TYPE (EmpathyContactSelectorDialog,
46     empathy_contact_selector_dialog,
47     GTK_TYPE_DIALOG)
48
49 typedef struct _EmpathyContactSelectorDialogPriv \
50           EmpathyContactSelectorDialogPriv;
51
52 struct _EmpathyContactSelectorDialogPriv {
53   GtkWidget *account_chooser_label;
54   GtkWidget *account_chooser;
55   GtkWidget *entry_id;
56   EmpathyContactManager *contact_manager;
57
58   gboolean show_account_chooser;
59 };
60
61 #define GET_PRIV(o) \
62   (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_CONTACT_SELECTOR_DIALOG, \
63     EmpathyContactSelectorDialogPriv))
64
65 enum {
66   PROP_0,
67   PROP_SHOW_ACCOUNT_CHOOSER
68 };
69
70 enum {
71   COMPLETION_COL_TEXT,
72   COMPLETION_COL_ID,
73   COMPLETION_COL_NAME,
74 } CompletionCol;
75
76 static void
77 contact_selector_dialog_account_changed_cb (GtkWidget *widget,
78     EmpathyContactSelectorDialog *dialog)
79 {
80   EmpathyContactSelectorDialogPriv *priv = GET_PRIV (dialog);
81   EmpathyAccountChooser *chooser;
82   TpConnection *connection;
83   GList *members;
84   GtkListStore *store;
85   GtkEntryCompletion *completion;
86
87   /* Remove completions */
88   completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry_id));
89   store = GTK_LIST_STORE (gtk_entry_completion_get_model (completion));
90   gtk_list_store_clear (store);
91
92   /* Get members of the new account */
93   chooser = EMPATHY_ACCOUNT_CHOOSER (priv->account_chooser);
94   connection = empathy_account_chooser_get_connection (chooser);
95   if (!connection)
96     return;
97
98   if (priv->show_account_chooser)
99     {
100       EmpathyTpContactList *contact_list;
101
102       contact_list = empathy_contact_manager_get_list (priv->contact_manager,
103                    connection);
104       members = empathy_contact_list_get_members (
105           EMPATHY_CONTACT_LIST (contact_list));
106     }
107   else
108     {
109       members = empathy_contact_list_get_members (
110           EMPATHY_CONTACT_LIST (priv->contact_manager));
111     }
112
113   /* Add members to the completion */
114   while (members)
115     {
116       EmpathyContact *contact = members->data;
117       GtkTreeIter iter;
118       gchar *tmpstr;
119
120       DEBUG ("Adding contact ID %s, Name %s",
121              empathy_contact_get_id (contact),
122              empathy_contact_get_name (contact));
123
124       tmpstr = g_strdup_printf ("%s (%s)",
125         empathy_contact_get_name (contact),
126         empathy_contact_get_id (contact));
127
128       gtk_list_store_insert_with_values (store, &iter, -1,
129         COMPLETION_COL_TEXT, tmpstr,
130         COMPLETION_COL_ID, empathy_contact_get_id (contact),
131         COMPLETION_COL_NAME, empathy_contact_get_name (contact),
132         -1);
133
134       g_free (tmpstr);
135
136       g_object_unref (contact);
137       members = g_list_delete_link (members, members);
138   }
139 }
140
141 static gboolean
142 contact_selector_dialog_match_selected_cb (GtkEntryCompletion *widget,
143     GtkTreeModel *model,
144     GtkTreeIter *iter,
145     EmpathyContactSelectorDialog *dialog)
146 {
147   EmpathyContactSelectorDialogPriv *priv = GET_PRIV (dialog);
148   gchar *id;
149
150   if (!iter || !model)
151     return FALSE;
152
153   gtk_tree_model_get (model, iter, COMPLETION_COL_ID, &id, -1);
154   gtk_entry_set_text (GTK_ENTRY (priv->entry_id), id);
155
156   DEBUG ("Got selected match **%s**", id);
157
158   g_free (id);
159
160   return TRUE;
161 }
162
163 static gboolean
164 contact_selector_dialog_match_func (GtkEntryCompletion *completion,
165     const gchar *key,
166     GtkTreeIter *iter,
167     gpointer user_data)
168 {
169   GtkTreeModel *model;
170   gchar *str, *lower;
171   gboolean v = FALSE;
172
173   model = gtk_entry_completion_get_model (completion);
174   if (!model || !iter)
175     return FALSE;
176
177   gtk_tree_model_get (model, iter, COMPLETION_COL_NAME, &str, -1);
178   lower = g_utf8_strdown (str, -1);
179   if (strstr (lower, key))
180     {
181       DEBUG ("Key %s is matching name **%s**", key, str);
182       v = TRUE;
183       goto out;
184     }
185   g_free (str);
186   g_free (lower);
187
188   gtk_tree_model_get (model, iter, COMPLETION_COL_ID, &str, -1);
189   lower = g_utf8_strdown (str, -1);
190   if (strstr (lower, key))
191     {
192       DEBUG ("Key %s is matching ID **%s**", key, str);
193       v = TRUE;
194       goto out;
195     }
196
197 out:
198   g_free (str);
199   g_free (lower);
200
201   return v;
202 }
203
204 static void
205 contact_selector_dialog_response_cb (GtkWidget *widget,
206     gint response,
207     EmpathyContactSelectorDialog *dialog)
208 {
209   EmpathyContactSelectorDialogPriv *priv = GET_PRIV (dialog);
210   TpConnection *connection;
211   const gchar *id;
212   EmpathyContactSelectorDialogClass *class = \
213     EMPATHY_CONTACT_SELECTOR_DIALOG_GET_CLASS (dialog);
214
215   connection = empathy_account_chooser_get_connection (
216     EMPATHY_ACCOUNT_CHOOSER (priv->account_chooser));
217   id = gtk_entry_get_text (GTK_ENTRY (priv->entry_id));
218   if (!connection || EMP_STR_EMPTY (id))
219     {
220       gtk_widget_destroy (widget);
221       return;
222     }
223
224   if (response == GTK_RESPONSE_ACCEPT)
225     {
226       class->got_response (dialog, connection, id);
227     }
228
229   gtk_widget_destroy (widget);
230 }
231
232 static void
233 contact_selector_change_state_button_cb  (GtkEditable *editable,
234     EmpathyContactSelectorDialog *dialog)
235 {
236   const gchar *id;
237   gboolean sensitive;
238
239   id = gtk_entry_get_text (GTK_ENTRY (editable));
240   sensitive = !EMP_STR_EMPTY (id);
241
242   gtk_widget_set_sensitive (dialog->button_action, sensitive);
243 }
244
245 static void
246 entry_activate_cb (GtkEntry *entry,
247     gpointer self)
248 {
249   const gchar *id;
250
251   id = gtk_entry_get_text (entry);
252   if (EMP_STR_EMPTY (id))
253     return;
254
255   gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT);
256 }
257
258 static gboolean
259 account_chooser_filter (TpAccount *account,
260     gpointer user_data)
261 {
262   EmpathyContactSelectorDialog *self = user_data;
263   EmpathyContactSelectorDialogClass *class = \
264       EMPATHY_CONTACT_SELECTOR_DIALOG_GET_CLASS (self);
265
266   if (class->account_filter == NULL)
267     return empathy_account_chooser_filter_is_connected (account, user_data);
268
269   return class->account_filter (self, account);
270 }
271
272 static void
273 empathy_contact_selector_dialog_init (EmpathyContactSelectorDialog *dialog)
274 {
275   EmpathyContactSelectorDialogPriv *priv = GET_PRIV (dialog);
276   GtkBuilder *gui;
277   gchar *filename;
278   GtkEntryCompletion *completion;
279   GtkListStore *model;
280   GtkWidget *content_area;
281   GtkWidget *table_contact;
282
283   dialog->vbox = gtk_vbox_new (FALSE, 3);
284
285   /* create a contact manager */
286   priv->contact_manager = empathy_contact_manager_dup_singleton ();
287
288   filename = empathy_file_lookup ("empathy-contact-selector-dialog.ui",
289           "libempathy-gtk");
290   gui = empathy_builder_get_file (filename,
291                 "table_contact", &table_contact,
292                 "account_chooser_label", &priv->account_chooser_label,
293                 "entry_id", &priv->entry_id,
294                 NULL);
295   g_free (filename);
296
297   empathy_builder_connect (gui, dialog,
298       "entry_id", "activate", entry_activate_cb,
299       NULL);
300
301   content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
302   gtk_container_add (GTK_CONTAINER (content_area), dialog->vbox);
303   gtk_box_pack_start (GTK_BOX (dialog->vbox), table_contact, TRUE, TRUE, 0);
304   gtk_widget_show (dialog->vbox);
305
306   gtk_dialog_add_button (GTK_DIALOG (dialog),
307     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
308
309   /* Tweak the dialog */
310   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
311
312   gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
313   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
314   gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
315
316   gtk_container_set_border_width (GTK_CONTAINER (dialog->vbox), 6);
317   gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
318
319   /* text completion */
320   completion = gtk_entry_completion_new ();
321   model = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
322   gtk_entry_completion_set_text_column (completion, COMPLETION_COL_TEXT);
323   gtk_entry_completion_set_match_func (completion,
324                contact_selector_dialog_match_func,
325                NULL, NULL);
326   gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (model));
327   gtk_entry_set_completion (GTK_ENTRY (priv->entry_id), completion);
328   g_signal_connect (completion, "match-selected",
329         G_CALLBACK (contact_selector_dialog_match_selected_cb),
330         dialog);
331   g_object_unref (completion);
332   g_object_unref (model);
333
334   g_signal_connect (dialog, "response",
335         G_CALLBACK (contact_selector_dialog_response_cb), dialog);
336
337   empathy_builder_connect (gui, dialog,
338              "entry_id", "changed", contact_selector_change_state_button_cb,
339              NULL);
340
341   g_object_unref (gui);
342
343   /* Create account chooser */
344   priv->account_chooser = empathy_account_chooser_new ();
345   gtk_table_attach_defaults (GTK_TABLE (table_contact),
346            priv->account_chooser,
347            1, 2, 0, 1);
348   empathy_account_chooser_set_filter (
349       EMPATHY_ACCOUNT_CHOOSER (priv->account_chooser),
350       account_chooser_filter,
351       dialog);
352   gtk_widget_show (priv->account_chooser);
353
354   contact_selector_dialog_account_changed_cb (priv->account_chooser, dialog);
355   g_signal_connect (priv->account_chooser, "changed",
356         G_CALLBACK (contact_selector_dialog_account_changed_cb),
357         dialog);
358 }
359
360 static void
361 empathy_contact_selector_dialog_get_property (GObject *self,
362     guint prop_id,
363     GValue *value,
364     GParamSpec *pspec)
365 {
366   EmpathyContactSelectorDialog *dialog = EMPATHY_CONTACT_SELECTOR_DIALOG (self);
367
368   switch (prop_id)
369     {
370       case PROP_SHOW_ACCOUNT_CHOOSER:
371         g_value_set_boolean (value,
372           empathy_contact_selector_dialog_get_show_account_chooser (dialog));
373         break;
374
375       default:
376         G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
377         break;
378     }
379 }
380
381 static void
382 empathy_contact_selector_dialog_set_property (GObject *self,
383     guint prop_id,
384     const GValue *value,
385     GParamSpec *pspec)
386 {
387   EmpathyContactSelectorDialog *dialog = EMPATHY_CONTACT_SELECTOR_DIALOG (self);
388
389   switch (prop_id)
390     {
391       case PROP_SHOW_ACCOUNT_CHOOSER:
392         empathy_contact_selector_dialog_set_show_account_chooser (dialog,
393             g_value_get_boolean (value));
394         break;
395
396       default:
397         G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
398         break;
399     }
400 }
401
402 static void
403 empathy_contact_selector_dialog_dispose (GObject *object)
404 {
405   EmpathyContactSelectorDialogPriv *priv = GET_PRIV (object);
406
407   if (priv->contact_manager != NULL) {
408     g_object_unref (priv->contact_manager);
409     priv->contact_manager = NULL;
410   }
411
412   if (G_OBJECT_CLASS (empathy_contact_selector_dialog_parent_class)->dispose)
413     G_OBJECT_CLASS (empathy_contact_selector_dialog_parent_class)->dispose (
414         object);
415 }
416
417 static void
418 empathy_contact_selector_dialog_class_init (
419     EmpathyContactSelectorDialogClass *class)
420 {
421   GObjectClass *object_class = G_OBJECT_CLASS (class);
422
423   g_type_class_add_private (class, sizeof (EmpathyContactSelectorDialogPriv));
424
425   object_class->dispose = empathy_contact_selector_dialog_dispose;
426   object_class->get_property = empathy_contact_selector_dialog_get_property;
427   object_class->set_property = empathy_contact_selector_dialog_set_property;
428
429   g_object_class_install_property (object_class, PROP_SHOW_ACCOUNT_CHOOSER,
430       g_param_spec_boolean ("show-account-chooser",
431         "Show Account Chooser",
432         "Whether or not this dialog should show an account chooser",
433         TRUE,
434         G_PARAM_READWRITE));
435 }
436
437 void
438 empathy_contact_selector_dialog_set_show_account_chooser (
439     EmpathyContactSelectorDialog *self,
440     gboolean show_account_chooser)
441 {
442   EmpathyContactSelectorDialogPriv *priv;
443
444   g_return_if_fail (EMPATHY_IS_CONTACT_SELECTOR_DIALOG (self));
445
446   priv = GET_PRIV (self);
447   priv->show_account_chooser = show_account_chooser;
448
449   gtk_widget_set_visible (priv->account_chooser_label, show_account_chooser);
450   gtk_widget_set_visible (priv->account_chooser, show_account_chooser);
451   contact_selector_dialog_account_changed_cb (priv->account_chooser, self);
452
453   g_object_notify (G_OBJECT (self), "show-account-chooser");
454 }
455
456 gboolean
457 empathy_contact_selector_dialog_get_show_account_chooser (
458     EmpathyContactSelectorDialog *self)
459 {
460   EmpathyContactSelectorDialogPriv *priv;
461
462   g_return_val_if_fail (EMPATHY_IS_CONTACT_SELECTOR_DIALOG (self), FALSE);
463
464   priv = GET_PRIV (self);
465   return priv->show_account_chooser;
466 }