]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-account-chooser.c
Add needed empathy.omf.in and set Milo Casagrande as maintainer. seriesid generated...
[empathy.git] / libempathy-gtk / empathy-account-chooser.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2005-2007 Imendio AB
4  * Copyright (C) 2007-2008 Collabora Ltd.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  * 
21  * Authors: Martyn Russell <martyn@imendio.com>
22  *          Xavier Claessens <xclaesse@gmail.com>
23  */
24
25 #include "config.h"
26
27 #include <string.h>
28
29 #include <glib/gi18n.h>
30 #include <gtk/gtk.h>
31 #include <glade/glade.h>
32
33 #include <libmissioncontrol/mc-account-monitor.h>
34 #include <libmissioncontrol/mission-control.h>
35
36 #include <libempathy/empathy-utils.h>
37
38 #include "empathy-ui-utils.h"
39 #include "empathy-account-chooser.h"
40
41 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_ACCOUNT_CHOOSER, EmpathyAccountChooserPriv))
42
43 typedef struct {
44         MissionControl                 *mc;
45         McAccountMonitor               *monitor;
46         gboolean                        set_active_item;
47         gboolean                        has_all_option;
48         EmpathyAccountChooserFilterFunc filter;
49         gpointer                        filter_data;
50         gpointer                        token;
51 } EmpathyAccountChooserPriv;
52
53 typedef struct {
54         EmpathyAccountChooser *chooser;
55         McAccount             *account;
56         gboolean               set;
57 } SetAccountData;
58
59 enum {
60         COL_ACCOUNT_IMAGE,
61         COL_ACCOUNT_TEXT,
62         COL_ACCOUNT_ENABLED, /* Usually tied to connected state */
63         COL_ACCOUNT_POINTER,
64         COL_ACCOUNT_COUNT
65 };
66
67 static void     account_chooser_finalize               (GObject                  *object);
68 static void     account_chooser_get_property           (GObject                  *object,
69                                                         guint                     param_id,
70                                                         GValue                   *value,
71                                                         GParamSpec               *pspec);
72 static void     account_chooser_set_property           (GObject                  *object,
73                                                         guint                     param_id,
74                                                         const GValue             *value,
75                                                         GParamSpec               *pspec);
76 static void     account_chooser_setup                  (EmpathyAccountChooser    *chooser);
77 static void     account_chooser_account_created_cb     (McAccountMonitor         *monitor,
78                                                         const gchar              *unique_name,
79                                                         EmpathyAccountChooser    *chooser);
80 static void     account_chooser_account_add_foreach    (McAccount                *account,
81                                                         EmpathyAccountChooser    *chooser);
82 static void     account_chooser_account_deleted_cb     (McAccountMonitor         *monitor,
83                                                         const gchar              *unique_name,
84                                                         EmpathyAccountChooser    *chooser);
85 static void     account_chooser_account_remove_foreach (McAccount                *account,
86                                                         EmpathyAccountChooser    *chooser);
87 static void     account_chooser_update_iter            (EmpathyAccountChooser    *chooser,
88                                                         GtkTreeIter              *iter);
89 static void     account_chooser_status_changed_cb      (MissionControl           *mc,
90                                                         TpConnectionStatus        status,
91                                                         McPresence                presence,
92                                                         TpConnectionStatusReason  reason,
93                                                         const gchar              *unique_name,
94                                                         EmpathyAccountChooser    *chooser);
95 static gboolean account_chooser_separator_func         (GtkTreeModel             *model,
96                                                         GtkTreeIter              *iter,
97                                                         EmpathyAccountChooser    *chooser);
98 static gboolean account_chooser_set_account_foreach    (GtkTreeModel             *model,
99                                                         GtkTreePath              *path,
100                                                         GtkTreeIter              *iter,
101                                                         SetAccountData           *data);
102
103 enum {
104         PROP_0,
105         PROP_HAS_ALL_OPTION,
106 };
107
108 G_DEFINE_TYPE (EmpathyAccountChooser, empathy_account_chooser, GTK_TYPE_COMBO_BOX);
109
110 static void
111 empathy_account_chooser_class_init (EmpathyAccountChooserClass *klass)
112 {
113         GObjectClass *object_class = G_OBJECT_CLASS (klass);
114
115         object_class->finalize = account_chooser_finalize;
116         object_class->get_property = account_chooser_get_property;
117         object_class->set_property = account_chooser_set_property;
118
119         g_object_class_install_property (object_class,
120                                          PROP_HAS_ALL_OPTION,
121                                          g_param_spec_boolean ("has-all-option",
122                                                                "Has All Option",
123                                                                "Have a separate option in the list to mean ALL accounts",
124                                                                FALSE,
125                                                                G_PARAM_READWRITE));
126
127         g_type_class_add_private (object_class, sizeof (EmpathyAccountChooserPriv));
128 }
129
130 static void
131 empathy_account_chooser_init (EmpathyAccountChooser *chooser)
132 {
133         EmpathyAccountChooserPriv *priv = GET_PRIV (chooser);
134
135         priv->set_active_item = FALSE;
136         priv->filter = NULL;
137         priv->filter_data = NULL;
138 }
139
140 static void
141 account_chooser_finalize (GObject *object)
142 {
143         EmpathyAccountChooser     *chooser;
144         EmpathyAccountChooserPriv *priv;
145
146         chooser = EMPATHY_ACCOUNT_CHOOSER (object);
147         priv = GET_PRIV (object);
148
149         g_signal_handlers_disconnect_by_func (priv->monitor,
150                                               account_chooser_account_created_cb,
151                                               chooser);
152         g_signal_handlers_disconnect_by_func (priv->monitor,
153                                               account_chooser_account_deleted_cb,
154                                               chooser);
155         empathy_disconnect_account_status_changed (priv->token);
156         g_object_unref (priv->mc);
157         g_object_unref (priv->monitor);
158
159         G_OBJECT_CLASS (empathy_account_chooser_parent_class)->finalize (object);
160 }
161
162 static void
163 account_chooser_get_property (GObject    *object,
164                               guint       param_id,
165                               GValue     *value,
166                               GParamSpec *pspec)
167 {
168         EmpathyAccountChooserPriv *priv;
169
170         priv = GET_PRIV (object);
171
172         switch (param_id) {
173         case PROP_HAS_ALL_OPTION:
174                 g_value_set_boolean (value, priv->has_all_option);
175                 break;
176         default:
177                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
178                 break;
179         };
180 }
181
182 static void
183 account_chooser_set_property (GObject      *object,
184                               guint         param_id,
185                               const GValue *value,
186                               GParamSpec   *pspec)
187 {
188         EmpathyAccountChooserPriv *priv;
189
190         priv = GET_PRIV (object);
191
192         switch (param_id) {
193         case PROP_HAS_ALL_OPTION:
194                 empathy_account_chooser_set_has_all_option (EMPATHY_ACCOUNT_CHOOSER (object),
195                                                            g_value_get_boolean (value));
196                 break;
197         default:
198                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
199                 break;
200         };
201 }
202
203 GtkWidget *
204 empathy_account_chooser_new (void)
205 {
206         EmpathyAccountChooserPriv *priv;
207         McAccountMonitor         *monitor;
208         GtkWidget                *chooser;
209
210         monitor = mc_account_monitor_new ();
211         chooser = g_object_new (EMPATHY_TYPE_ACCOUNT_CHOOSER, NULL);
212
213         priv = GET_PRIV (chooser);
214
215         priv->mc = empathy_mission_control_new ();
216         priv->monitor = mc_account_monitor_new ();
217
218         g_signal_connect (priv->monitor, "account-created",
219                           G_CALLBACK (account_chooser_account_created_cb),
220                           chooser);
221         g_signal_connect (priv->monitor, "account-deleted",
222                           G_CALLBACK (account_chooser_account_deleted_cb),
223                           chooser);
224         priv->token = empathy_connect_to_account_status_changed (priv->mc,
225                                                    G_CALLBACK (account_chooser_status_changed_cb),
226                                                    chooser, NULL);
227
228         account_chooser_setup (EMPATHY_ACCOUNT_CHOOSER (chooser));
229
230         return chooser;
231 }
232
233 McAccount *
234 empathy_account_chooser_get_account (EmpathyAccountChooser *chooser)
235 {
236         EmpathyAccountChooserPriv *priv;
237         McAccount                *account;
238         GtkTreeModel             *model;
239         GtkTreeIter               iter;
240
241         g_return_val_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser), NULL);
242
243         priv = GET_PRIV (chooser);
244
245         model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser));
246         gtk_combo_box_get_active_iter (GTK_COMBO_BOX (chooser), &iter);
247
248         gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, &account, -1);
249
250         return account;
251 }
252
253 gboolean
254 empathy_account_chooser_set_account (EmpathyAccountChooser *chooser,
255                                      McAccount             *account)
256 {
257         GtkComboBox    *combobox;
258         GtkTreeModel   *model;
259         GtkTreeIter     iter;
260         SetAccountData  data;
261
262         g_return_val_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser), FALSE);
263
264         combobox = GTK_COMBO_BOX (chooser);
265         model = gtk_combo_box_get_model (combobox);
266         gtk_combo_box_get_active_iter (combobox, &iter);
267
268         data.chooser = chooser;
269         data.account = account;
270
271         gtk_tree_model_foreach (model,
272                                 (GtkTreeModelForeachFunc) account_chooser_set_account_foreach,
273                                 &data);
274
275         return data.set;
276 }
277
278 gboolean
279 empathy_account_chooser_get_has_all_option (EmpathyAccountChooser *chooser)
280 {
281         EmpathyAccountChooserPriv *priv;
282
283         g_return_val_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser), FALSE);
284
285         priv = GET_PRIV (chooser);
286         
287         return priv->has_all_option;
288 }
289
290 void
291 empathy_account_chooser_set_has_all_option (EmpathyAccountChooser *chooser,
292                                            gboolean              has_all_option)
293 {
294         EmpathyAccountChooserPriv *priv;
295         GtkComboBox              *combobox;
296         GtkListStore             *store;
297         GtkTreeModel             *model;
298         GtkTreeIter               iter;
299
300         g_return_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser));
301
302         priv = GET_PRIV (chooser);
303
304         if (priv->has_all_option == has_all_option) {
305                 return;
306         }
307
308         combobox = GTK_COMBO_BOX (chooser);
309         model = gtk_combo_box_get_model (combobox);
310         store = GTK_LIST_STORE (model);
311
312         priv->has_all_option = has_all_option;
313
314         /*
315          * The first 2 options are the ALL and separator
316          */
317
318         if (has_all_option) {
319                 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (chooser), 
320                                                       (GtkTreeViewRowSeparatorFunc)
321                                                       account_chooser_separator_func,
322                                                       chooser, 
323                                                       NULL);
324
325                 gtk_list_store_prepend (store, &iter);
326                 gtk_list_store_set (store, &iter, 
327                                     COL_ACCOUNT_TEXT, NULL,
328                                     COL_ACCOUNT_ENABLED, TRUE,
329                                     COL_ACCOUNT_POINTER, NULL,
330                                     -1);
331
332                 gtk_list_store_prepend (store, &iter);
333                 gtk_list_store_set (store, &iter, 
334                                     COL_ACCOUNT_TEXT, _("All"), 
335                                     COL_ACCOUNT_ENABLED, TRUE,
336                                     COL_ACCOUNT_POINTER, NULL,
337                                     -1);
338         } else {
339                 if (gtk_tree_model_get_iter_first (model, &iter)) {
340                         if (gtk_list_store_remove (GTK_LIST_STORE (model), &iter)) {
341                                 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
342                         }
343                 }
344
345                 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (chooser), 
346                                                       (GtkTreeViewRowSeparatorFunc)
347                                                       NULL,
348                                                       NULL, 
349                                                       NULL);
350         }
351
352         g_object_notify (G_OBJECT (chooser), "has-all-option");
353 }
354
355 static void
356 account_chooser_setup (EmpathyAccountChooser *chooser)
357 {
358         EmpathyAccountChooserPriv *priv;
359         GList                    *accounts;
360         GtkListStore             *store;
361         GtkCellRenderer          *renderer;
362         GtkComboBox              *combobox;
363
364         priv = GET_PRIV (chooser);
365
366         /* Set up combo box with new store */
367         combobox = GTK_COMBO_BOX (chooser);
368
369         gtk_cell_layout_clear (GTK_CELL_LAYOUT (combobox));
370
371         store = gtk_list_store_new (COL_ACCOUNT_COUNT,
372                                     G_TYPE_STRING,    /* Image */
373                                     G_TYPE_STRING,    /* Name */
374                                     G_TYPE_BOOLEAN,   /* Enabled */
375                                     MC_TYPE_ACCOUNT);
376
377         gtk_combo_box_set_model (combobox, GTK_TREE_MODEL (store));
378
379         renderer = gtk_cell_renderer_pixbuf_new ();
380         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), renderer, FALSE);
381         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer,
382                                         "icon-name", COL_ACCOUNT_IMAGE,
383                                         "sensitive", COL_ACCOUNT_ENABLED,
384                                         NULL);
385         g_object_set (renderer, "stock-size", GTK_ICON_SIZE_BUTTON, NULL);
386
387         renderer = gtk_cell_renderer_text_new ();
388         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), renderer, TRUE);
389         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer,
390                                         "text", COL_ACCOUNT_TEXT,
391                                         "sensitive", COL_ACCOUNT_ENABLED,
392                                         NULL);
393
394         /* Populate accounts */
395         accounts = mc_accounts_list ();
396         g_list_foreach (accounts,
397                         (GFunc) account_chooser_account_add_foreach,
398                         chooser);
399
400         mc_accounts_list_free (accounts);
401         g_object_unref (store);
402 }
403
404 static void
405 account_chooser_account_created_cb (McAccountMonitor     *monitor,
406                                     const gchar          *unique_name,
407                                     EmpathyAccountChooser *chooser)
408 {
409         McAccount *account;
410
411         account = mc_account_lookup (unique_name);
412         account_chooser_account_add_foreach (account, chooser);
413         g_object_unref (account);
414 }
415
416 static void
417 account_chooser_account_add_foreach (McAccount             *account,
418                                      EmpathyAccountChooser *chooser)
419 {
420         GtkListStore *store;
421         GtkComboBox  *combobox;
422         GtkTreeIter   iter;
423         gint          position;
424
425         combobox = GTK_COMBO_BOX (chooser);
426         store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox));
427
428         position = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL);
429         gtk_list_store_insert_with_values (store, &iter, position,
430                                            COL_ACCOUNT_POINTER, account,
431                                            -1);
432         account_chooser_update_iter (chooser, &iter);
433 }
434
435 static void
436 account_chooser_account_deleted_cb (McAccountMonitor     *monitor,
437                                     const gchar          *unique_name,
438                                     EmpathyAccountChooser *chooser)
439 {
440         McAccount *account;
441
442         account = mc_account_lookup (unique_name);
443         account_chooser_account_remove_foreach (account, chooser);
444         g_object_unref (account);
445 }
446
447 typedef struct {
448         McAccount   *account;
449         GtkTreeIter *iter;
450         gboolean     found;
451 } FindAccountData;
452
453 static gboolean
454 account_chooser_find_account_foreach (GtkTreeModel *model,
455                                       GtkTreePath  *path,
456                                       GtkTreeIter  *iter,
457                                       gpointer      user_data)
458 {
459         FindAccountData *data = user_data;
460         McAccount       *account;
461
462         gtk_tree_model_get (model, iter, COL_ACCOUNT_POINTER, &account, -1);
463
464         if (empathy_account_equal (account, data->account)) {
465                 data->found = TRUE;
466                 *(data->iter) = *iter;
467                 g_object_unref (account);
468
469                 return TRUE;
470         }
471
472         g_object_unref (account);
473
474         return FALSE;
475 }
476
477 static gboolean
478 account_chooser_find_account (EmpathyAccountChooser *chooser,
479                               McAccount             *account,
480                               GtkTreeIter           *iter)
481 {
482         GtkListStore    *store;
483         GtkComboBox     *combobox;
484         FindAccountData  data;
485
486         combobox = GTK_COMBO_BOX (chooser);
487         store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox));
488
489         data.account = account;
490         data.iter = iter;
491         gtk_tree_model_foreach (GTK_TREE_MODEL (store),
492                                 account_chooser_find_account_foreach,
493                                 &data);
494
495         return data.found;
496 }
497
498 static void
499 account_chooser_account_remove_foreach (McAccount            *account,
500                                         EmpathyAccountChooser *chooser)
501 {
502         GtkListStore *store;
503         GtkComboBox  *combobox;
504         GtkTreeIter   iter;
505
506         combobox = GTK_COMBO_BOX (chooser);
507         store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox));
508
509         if (account_chooser_find_account (chooser, account, &iter)) {
510                 gtk_list_store_remove (store, &iter);
511         }
512 }
513
514 static void
515 account_chooser_update_iter (EmpathyAccountChooser *chooser,
516                              GtkTreeIter           *iter)
517 {
518         EmpathyAccountChooserPriv *priv;
519         GtkListStore              *store;
520         GtkComboBox               *combobox;
521         McAccount                 *account;
522         const gchar               *icon_name;
523         gboolean                   is_enabled = TRUE;
524
525         priv = GET_PRIV (chooser);
526
527         combobox = GTK_COMBO_BOX (chooser);
528         store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox));
529
530         gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
531                             COL_ACCOUNT_POINTER, &account,
532                             -1);
533
534         icon_name = empathy_icon_name_from_account (account);
535         if (priv->filter) {
536                 is_enabled = priv->filter (account, priv->filter_data);
537         }
538
539         gtk_list_store_set (store, iter,
540                             COL_ACCOUNT_IMAGE, icon_name,
541                             COL_ACCOUNT_TEXT, mc_account_get_display_name (account),
542                             COL_ACCOUNT_ENABLED, is_enabled,
543                             -1);
544
545         /* set first connected account as active account */
546         if (priv->set_active_item == FALSE && is_enabled) {
547                 priv->set_active_item = TRUE;
548                 gtk_combo_box_set_active_iter (combobox, iter);
549         }
550
551         g_object_unref (account);
552 }
553
554 static void
555 account_chooser_status_changed_cb (MissionControl           *mc,
556                                    TpConnectionStatus        status,
557                                    McPresence                presence,
558                                    TpConnectionStatusReason  reason,
559                                    const gchar              *unique_name,
560                                    EmpathyAccountChooser    *chooser)
561 {
562         McAccount   *account;
563         GtkTreeIter  iter;
564
565         account = mc_account_lookup (unique_name);
566         if (account_chooser_find_account (chooser, account, &iter)) {
567                 account_chooser_update_iter (chooser, &iter);
568         }
569         g_object_unref (account);
570 }
571
572 static gboolean
573 account_chooser_separator_func (GtkTreeModel         *model,
574                                 GtkTreeIter          *iter,
575                                 EmpathyAccountChooser *chooser)
576 {
577         EmpathyAccountChooserPriv *priv;
578         gchar                    *text;
579         gboolean                  is_separator;
580
581         priv = GET_PRIV (chooser);
582         
583         if (!priv->has_all_option) {
584                 return FALSE;
585         }
586         
587         gtk_tree_model_get (model, iter, COL_ACCOUNT_TEXT, &text, -1);
588         is_separator = text == NULL;
589         g_free (text);
590
591         return is_separator;
592 }
593
594 static gboolean
595 account_chooser_set_account_foreach (GtkTreeModel   *model,
596                                      GtkTreePath    *path,
597                                      GtkTreeIter    *iter,
598                                      SetAccountData *data)
599 {
600         McAccount *account;
601         gboolean   equal;
602
603         gtk_tree_model_get (model, iter, COL_ACCOUNT_POINTER, &account, -1);
604
605         /* Special case so we can make it possible to select the All option */
606         if ((data->account == NULL) != (account == NULL)) {
607                 equal = FALSE;
608         }
609         else if (data->account == account) {
610                 equal = TRUE;
611         } else {
612                 equal = empathy_account_equal (data->account, account);
613         }
614
615         if (account) {
616                 g_object_unref (account);
617         }
618
619         if (equal) {
620                 GtkComboBox *combobox;
621
622                 combobox = GTK_COMBO_BOX (data->chooser);
623                 gtk_combo_box_set_active_iter (combobox, iter);
624
625                 data->set = TRUE;
626         }
627
628         return equal;
629 }
630
631 static gboolean
632 account_chooser_filter_foreach (GtkTreeModel *model,
633                                 GtkTreePath  *path,
634                                 GtkTreeIter  *iter,
635                                 gpointer      chooser)
636 {
637         account_chooser_update_iter (chooser, iter);
638         return FALSE;
639 }
640
641 void
642 empathy_account_chooser_set_filter (EmpathyAccountChooser           *chooser,
643                                     EmpathyAccountChooserFilterFunc  filter,
644                                     gpointer                         user_data)
645 {
646         EmpathyAccountChooserPriv *priv;
647         GtkTreeModel *model;
648
649         g_return_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser));
650
651         priv = GET_PRIV (chooser);
652
653         priv->filter = filter;
654         priv->filter_data = user_data;
655
656         /* Refilter existing data */
657         priv->set_active_item = FALSE;
658         model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser));
659         gtk_tree_model_foreach (model, account_chooser_filter_foreach, chooser);
660 }
661
662 gboolean
663 empathy_account_chooser_filter_is_connected (McAccount *account,
664                                              gpointer   user_data)
665 {
666         MissionControl     *mc;
667         TpConnectionStatus  status;
668
669         g_return_val_if_fail (MC_IS_ACCOUNT (account), FALSE);
670
671         mc = empathy_mission_control_new ();
672         status = mission_control_get_connection_status (mc, account, NULL);
673         g_object_unref (mc);
674
675         return status == TP_CONNECTION_STATUS_CONNECTED;
676 }
677