]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-account-chooser.c
account-widget-sip: unsensitive the STUN labels as well when STUN is disabled
[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., 51 Franklin St, Fifth Floor,
19  * Boston, MA  02110-1301  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-lib.h>
30 #include <gtk/gtk.h>
31
32 #include <telepathy-glib/account-manager.h>
33
34 #include <libempathy/empathy-utils.h>
35
36 #include "empathy-ui-utils.h"
37 #include "empathy-account-chooser.h"
38
39 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
40 #include <libempathy/empathy-debug.h>
41
42 /**
43  * SECTION:empathy-account-chooser
44  * @title:EmpathyAccountChooser
45  * @short_description: A widget used to choose from a list of accounts
46  * @include: libempathy-gtk/empathy-account-chooser.h
47  *
48  * #EmpathyAccountChooser is a widget which extends #GtkComboBox to provide
49  * a chooser of available accounts.
50  */
51
52 /**
53  * EmpathyAccountChooser:
54  * @parent: parent object
55  *
56  * Widget which extends #GtkComboBox to provide a chooser of available accounts.
57  */
58
59 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccountChooser)
60 typedef struct {
61         TpAccountManager               *manager;
62         gboolean                        set_active_item;
63         gboolean                        account_manually_set;
64         gboolean                        has_all_option;
65         EmpathyAccountChooserFilterFunc filter;
66         gpointer                        filter_data;
67         gboolean                        ready;
68 } EmpathyAccountChooserPriv;
69
70 typedef struct {
71         EmpathyAccountChooser *chooser;
72         TpAccount             *account;
73         gboolean               set;
74 } SetAccountData;
75
76 enum {
77         COL_ACCOUNT_IMAGE,
78         COL_ACCOUNT_TEXT,
79         COL_ACCOUNT_ENABLED, /* Usually tied to connected state */
80         COL_ACCOUNT_POINTER,
81         COL_ACCOUNT_COUNT
82 };
83
84 static void     account_chooser_finalize               (GObject                  *object);
85 static void     account_chooser_get_property           (GObject                  *object,
86                                                         guint                     param_id,
87                                                         GValue                   *value,
88                                                         GParamSpec               *pspec);
89 static void     account_chooser_set_property           (GObject                  *object,
90                                                         guint                     param_id,
91                                                         const GValue             *value,
92                                                         GParamSpec               *pspec);
93 static void     account_chooser_setup                  (EmpathyAccountChooser    *chooser);
94 static void     account_chooser_account_validity_changed_cb (TpAccountManager    *manager,
95                                                         TpAccount                *account,
96                                                         gboolean                  valid,
97                                                         EmpathyAccountChooser    *chooser);
98 static void     account_chooser_account_add_foreach    (TpAccount                *account,
99                                                         EmpathyAccountChooser    *chooser);
100 static void     account_chooser_account_removed_cb     (TpAccountManager         *manager,
101                                                         TpAccount                *account,
102                                                         EmpathyAccountChooser    *chooser);
103 static void     account_chooser_account_remove_foreach (TpAccount                *account,
104                                                         EmpathyAccountChooser    *chooser);
105 static void     account_chooser_update_iter            (EmpathyAccountChooser    *chooser,
106                                                         GtkTreeIter              *iter);
107 static void     account_chooser_status_changed_cb      (TpAccount  *account,
108                                                         guint       old_status,
109                                                         guint       new_status,
110                                                         guint       reason,
111                                                         gchar      *dbus_error_name,
112                                                         GHashTable *details,
113                                                         gpointer    user_data);
114 static gboolean account_chooser_separator_func         (GtkTreeModel             *model,
115                                                         GtkTreeIter              *iter,
116                                                         EmpathyAccountChooser    *chooser);
117 static gboolean account_chooser_set_account_foreach    (GtkTreeModel             *model,
118                                                         GtkTreePath              *path,
119                                                         GtkTreeIter              *iter,
120                                                         SetAccountData           *data);
121
122 enum {
123         PROP_0,
124         PROP_HAS_ALL_OPTION,
125 };
126
127 enum {
128         READY,
129         LAST_SIGNAL
130 };
131
132 static guint signals[LAST_SIGNAL] = { 0 };
133
134 G_DEFINE_TYPE (EmpathyAccountChooser, empathy_account_chooser, GTK_TYPE_COMBO_BOX);
135
136 static void
137 empathy_account_chooser_class_init (EmpathyAccountChooserClass *klass)
138 {
139         GObjectClass *object_class = G_OBJECT_CLASS (klass);
140
141         object_class->finalize = account_chooser_finalize;
142         object_class->get_property = account_chooser_get_property;
143         object_class->set_property = account_chooser_set_property;
144
145         /**
146          * EmpathyAccountChooser:has-all-option:
147          *
148          * Have an additional option in the list to mean all accounts.
149          */
150         g_object_class_install_property (object_class,
151                                          PROP_HAS_ALL_OPTION,
152                                          g_param_spec_boolean ("has-all-option",
153                                                                "Has All Option",
154                                                                "Have a separate option in the list to mean ALL accounts",
155                                                                FALSE,
156                                                                G_PARAM_READWRITE));
157
158         signals[READY] =
159                 g_signal_new ("ready",
160                               G_OBJECT_CLASS_TYPE (object_class),
161                               G_SIGNAL_RUN_LAST,
162                               0,
163                               NULL, NULL,
164                               g_cclosure_marshal_VOID__VOID,
165                               G_TYPE_NONE,
166                               0);
167
168         g_type_class_add_private (object_class, sizeof (EmpathyAccountChooserPriv));
169 }
170
171 static void
172 empathy_account_chooser_init (EmpathyAccountChooser *chooser)
173 {
174         EmpathyAccountChooserPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (chooser,
175                 EMPATHY_TYPE_ACCOUNT_CHOOSER, EmpathyAccountChooserPriv);
176
177         chooser->priv = priv;
178         priv->set_active_item = FALSE;
179         priv->account_manually_set = FALSE;
180         priv->filter = NULL;
181         priv->filter_data = NULL;
182
183         priv->manager = tp_account_manager_dup ();
184
185         g_signal_connect (priv->manager, "account-validity-changed",
186                           G_CALLBACK (account_chooser_account_validity_changed_cb),
187                           chooser);
188         g_signal_connect (priv->manager, "account-removed",
189                           G_CALLBACK (account_chooser_account_removed_cb),
190                           chooser);
191
192         account_chooser_setup (EMPATHY_ACCOUNT_CHOOSER (chooser));
193 }
194
195 static void
196 account_chooser_finalize (GObject *object)
197 {
198         EmpathyAccountChooserPriv *priv = GET_PRIV (object);
199
200         g_signal_handlers_disconnect_by_func (priv->manager,
201                                               account_chooser_account_validity_changed_cb,
202                                               object);
203         g_signal_handlers_disconnect_by_func (priv->manager,
204                                               account_chooser_account_removed_cb,
205                                               object);
206         g_object_unref (priv->manager);
207
208         G_OBJECT_CLASS (empathy_account_chooser_parent_class)->finalize (object);
209 }
210
211 static void
212 account_chooser_get_property (GObject    *object,
213                               guint       param_id,
214                               GValue     *value,
215                               GParamSpec *pspec)
216 {
217         EmpathyAccountChooserPriv *priv;
218
219         priv = GET_PRIV (object);
220
221         switch (param_id) {
222         case PROP_HAS_ALL_OPTION:
223                 g_value_set_boolean (value, priv->has_all_option);
224                 break;
225         default:
226                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
227                 break;
228         };
229 }
230
231 static void
232 account_chooser_set_property (GObject      *object,
233                               guint         param_id,
234                               const GValue *value,
235                               GParamSpec   *pspec)
236 {
237         EmpathyAccountChooserPriv *priv;
238
239         priv = GET_PRIV (object);
240
241         switch (param_id) {
242         case PROP_HAS_ALL_OPTION:
243                 empathy_account_chooser_set_has_all_option (EMPATHY_ACCOUNT_CHOOSER (object),
244                                                            g_value_get_boolean (value));
245                 break;
246         default:
247                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
248                 break;
249         };
250 }
251
252 /**
253  * empathy_account_chooser_new:
254  *
255  * Creates a new #EmpathyAccountChooser.
256  *
257  * Return value: A new #EmpathyAccountChooser
258  */
259 GtkWidget *
260 empathy_account_chooser_new (void)
261 {
262         GtkWidget                *chooser;
263
264         chooser = g_object_new (EMPATHY_TYPE_ACCOUNT_CHOOSER, NULL);
265
266         return chooser;
267 }
268
269 /**
270  * empathy_account_chooser_dup_account:
271  * @chooser: an #EmpathyAccountChooser
272  *
273  * Returns the account which is currently selected in the chooser or %NULL
274  * if there is no account selected. The #TpAccount returned should be
275  * unrefed with g_object_unref() when finished with.
276  *
277  * Return value: a new ref to the #TpAccount currently selected, or %NULL.
278  */
279 TpAccount *
280 empathy_account_chooser_dup_account (EmpathyAccountChooser *chooser)
281 {
282         EmpathyAccountChooserPriv *priv;
283         TpAccount                 *account;
284         GtkTreeModel             *model;
285         GtkTreeIter               iter;
286
287         g_return_val_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser), NULL);
288
289         priv = GET_PRIV (chooser);
290
291         model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser));
292         if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (chooser), &iter)) {
293                 return NULL;
294         }
295
296         gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, &account, -1);
297
298         return account;
299 }
300
301 /**
302  * empathy_account_chooser_get_connection:
303  * @chooser: an #EmpathyAccountChooser
304  *
305  * Returns a borrowed reference to the #TpConnection associated with the
306  * account currently selected. The caller must reference the returned object with
307  * g_object_ref() if it will be kept
308  *
309  * Return value: a borrowed reference to the #TpConnection associated with the
310  * account curently selected.
311  */
312 TpConnection *
313 empathy_account_chooser_get_connection (EmpathyAccountChooser *chooser)
314 {
315         EmpathyAccountChooserPriv *priv;
316         TpAccount                 *account;
317         TpConnection              *connection;
318
319         g_return_val_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser), NULL);
320
321         priv = GET_PRIV (chooser);
322
323         account = empathy_account_chooser_dup_account (chooser);
324
325         /* if the returned account is NULL, then the account manager probably
326          * hasn't been prepared yet. It should be safe to return NULL here
327          * though. */
328         if (account == NULL) {
329                 return NULL;
330         }
331
332         connection = tp_account_get_connection (account);
333         g_object_unref (account);
334
335         return connection;
336 }
337
338 /**
339  * empathy_account_chooser_set_account:
340  * @chooser: an #EmpathyAccountChooser
341  * @account: a #TpAccount
342  *
343  * Sets the currently selected account to @account, if it exists in the list.
344  *
345  * Return value: whether the chooser was set to @account.
346  */
347 gboolean
348 empathy_account_chooser_set_account (EmpathyAccountChooser *chooser,
349                                      TpAccount             *account)
350 {
351         EmpathyAccountChooserPriv *priv;
352         GtkComboBox    *combobox;
353         GtkTreeModel   *model;
354         GtkTreeIter     iter;
355         SetAccountData  data;
356
357         g_return_val_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser), FALSE);
358
359         priv = GET_PRIV (chooser);
360
361         combobox = GTK_COMBO_BOX (chooser);
362         model = gtk_combo_box_get_model (combobox);
363         gtk_combo_box_get_active_iter (combobox, &iter);
364
365         data.chooser = chooser;
366         data.account = account;
367
368         gtk_tree_model_foreach (model,
369                                 (GtkTreeModelForeachFunc) account_chooser_set_account_foreach,
370                                 &data);
371
372         priv->account_manually_set = data.set;
373
374         return data.set;
375 }
376
377 /**
378  * empathy_account_chooser_get_has_all_option:
379  * @chooser: an #EmpathyAccountChooser
380  *
381  * Returns whether @chooser has the #EmpathyAccountChooser:has-all-option property
382  * set to true.
383  *
384  * Return value: whether @chooser has the #EmpathyAccountChooser:has-all-option property
385  * enabled.
386  */
387 gboolean
388 empathy_account_chooser_get_has_all_option (EmpathyAccountChooser *chooser)
389 {
390         EmpathyAccountChooserPriv *priv;
391
392         g_return_val_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser), FALSE);
393
394         priv = GET_PRIV (chooser);
395
396         return priv->has_all_option;
397 }
398
399 /**
400  * empathy_account_chooser_set_has_all_option:
401  * @chooser: an #EmpathyAccountChooser
402  * @has_all_option: a new value for the #EmpathyAccountChooser:has-all-option property
403  *
404  * Sets the #EmpathyAccountChooser:has-all-option property.
405  */
406 void
407 empathy_account_chooser_set_has_all_option (EmpathyAccountChooser *chooser,
408                                            gboolean              has_all_option)
409 {
410         EmpathyAccountChooserPriv *priv;
411         GtkComboBox              *combobox;
412         GtkListStore             *store;
413         GtkTreeModel             *model;
414         GtkTreeIter               iter;
415
416         g_return_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser));
417
418         priv = GET_PRIV (chooser);
419
420         if (priv->has_all_option == has_all_option) {
421                 return;
422         }
423
424         combobox = GTK_COMBO_BOX (chooser);
425         model = gtk_combo_box_get_model (combobox);
426         store = GTK_LIST_STORE (model);
427
428         priv->has_all_option = has_all_option;
429
430         /*
431          * The first 2 options are the ALL and separator
432          */
433
434         if (has_all_option) {
435                 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (chooser),
436                                                       (GtkTreeViewRowSeparatorFunc)
437                                                       account_chooser_separator_func,
438                                                       chooser,
439                                                       NULL);
440
441                 gtk_list_store_prepend (store, &iter);
442                 gtk_list_store_set (store, &iter,
443                                     COL_ACCOUNT_TEXT, NULL,
444                                     COL_ACCOUNT_ENABLED, TRUE,
445                                     COL_ACCOUNT_POINTER, NULL,
446                                     -1);
447
448                 gtk_list_store_prepend (store, &iter);
449                 gtk_list_store_set (store, &iter,
450                                     COL_ACCOUNT_TEXT, _("All"),
451                                     COL_ACCOUNT_ENABLED, TRUE,
452                                     COL_ACCOUNT_POINTER, NULL,
453                                     -1);
454         } else {
455                 if (gtk_tree_model_get_iter_first (model, &iter)) {
456                         if (gtk_list_store_remove (GTK_LIST_STORE (model), &iter)) {
457                                 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
458                         }
459                 }
460
461                 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (chooser),
462                                                       (GtkTreeViewRowSeparatorFunc)
463                                                       NULL,
464                                                       NULL,
465                                                       NULL);
466         }
467
468         g_object_notify (G_OBJECT (chooser), "has-all-option");
469 }
470
471 static void
472 account_manager_prepared_cb (GObject *source_object,
473                              GAsyncResult *result,
474                              gpointer user_data)
475 {
476         GList *accounts, *l;
477         TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object);
478         EmpathyAccountChooser *chooser = user_data;
479         EmpathyAccountChooserPriv *priv = GET_PRIV (chooser);
480         GError *error = NULL;
481
482         if (!tp_account_manager_prepare_finish (manager, result, &error)) {
483                 DEBUG ("Failed to prepare account manager: %s", error->message);
484                 g_error_free (error);
485                 return;
486         }
487
488         accounts = tp_account_manager_get_valid_accounts (manager);
489
490         for (l = accounts; l != NULL; l = l->next) {
491                 TpAccount *account = l->data;
492
493                 account_chooser_account_add_foreach (account, chooser);
494
495                 empathy_signal_connect_weak (account, "status-changed",
496                                              G_CALLBACK (account_chooser_status_changed_cb),
497                                              G_OBJECT (chooser));
498         }
499
500         g_list_free (accounts);
501
502         priv->ready = TRUE;
503         g_signal_emit (chooser, signals[READY], 0);
504 }
505
506 static void
507 account_chooser_setup (EmpathyAccountChooser *chooser)
508 {
509         EmpathyAccountChooserPriv *priv;
510         GtkListStore             *store;
511         GtkCellRenderer          *renderer;
512         GtkComboBox              *combobox;
513
514         priv = GET_PRIV (chooser);
515
516         /* Set up combo box with new store */
517         combobox = GTK_COMBO_BOX (chooser);
518
519         gtk_cell_layout_clear (GTK_CELL_LAYOUT (combobox));
520
521         store = gtk_list_store_new (COL_ACCOUNT_COUNT,
522                                     G_TYPE_STRING,    /* Image */
523                                     G_TYPE_STRING,    /* Name */
524                                     G_TYPE_BOOLEAN,   /* Enabled */
525                                     TP_TYPE_ACCOUNT);
526
527         gtk_combo_box_set_model (combobox, GTK_TREE_MODEL (store));
528
529         renderer = gtk_cell_renderer_pixbuf_new ();
530         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), renderer, FALSE);
531         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer,
532                                         "icon-name", COL_ACCOUNT_IMAGE,
533                                         "sensitive", COL_ACCOUNT_ENABLED,
534                                         NULL);
535         g_object_set (renderer, "stock-size", GTK_ICON_SIZE_BUTTON, NULL);
536
537         renderer = gtk_cell_renderer_text_new ();
538         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), renderer, TRUE);
539         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer,
540                                         "text", COL_ACCOUNT_TEXT,
541                                         "sensitive", COL_ACCOUNT_ENABLED,
542                                         NULL);
543
544         /* Populate accounts */
545         tp_account_manager_prepare_async (priv->manager, NULL,
546                                           account_manager_prepared_cb, chooser);
547
548         g_object_unref (store);
549 }
550
551 static void
552 account_chooser_account_validity_changed_cb (TpAccountManager      *manager,
553                                              TpAccount             *account,
554                                              gboolean               valid,
555                                              EmpathyAccountChooser *chooser)
556 {
557         if (valid) {
558                 account_chooser_account_add_foreach (account, chooser);
559         } else {
560                 account_chooser_account_remove_foreach (account, chooser);
561         }
562 }
563
564 static void
565 account_chooser_account_add_foreach (TpAccount             *account,
566                                      EmpathyAccountChooser *chooser)
567 {
568         GtkListStore *store;
569         GtkComboBox  *combobox;
570         GtkTreeIter   iter;
571         gint          position;
572
573         combobox = GTK_COMBO_BOX (chooser);
574         store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox));
575
576         position = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL);
577         gtk_list_store_insert_with_values (store, &iter, position,
578                                            COL_ACCOUNT_POINTER, account,
579                                            -1);
580         account_chooser_update_iter (chooser, &iter);
581 }
582
583 static void
584 account_chooser_account_removed_cb (TpAccountManager      *manager,
585                                     TpAccount             *account,
586                                     EmpathyAccountChooser *chooser)
587 {
588         account_chooser_account_remove_foreach (account, chooser);
589 }
590
591 typedef struct {
592         TpAccount   *account;
593         GtkTreeIter *iter;
594         gboolean     found;
595 } FindAccountData;
596
597 static gboolean
598 account_chooser_find_account_foreach (GtkTreeModel *model,
599                                       GtkTreePath  *path,
600                                       GtkTreeIter  *iter,
601                                       gpointer      user_data)
602 {
603         FindAccountData *data = user_data;
604         TpAccount  *account;
605
606         gtk_tree_model_get (model, iter, COL_ACCOUNT_POINTER, &account, -1);
607
608         if (account == data->account) {
609                 data->found = TRUE;
610                 *(data->iter) = *iter;
611                 g_object_unref (account);
612
613                 return TRUE;
614         }
615
616         g_object_unref (account);
617
618         return FALSE;
619 }
620
621 static gboolean
622 account_chooser_find_account (EmpathyAccountChooser *chooser,
623                               TpAccount             *account,
624                               GtkTreeIter           *iter)
625 {
626         GtkListStore    *store;
627         GtkComboBox     *combobox;
628         FindAccountData  data;
629
630         combobox = GTK_COMBO_BOX (chooser);
631         store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox));
632
633         data.account = account;
634         data.iter = iter;
635         gtk_tree_model_foreach (GTK_TREE_MODEL (store),
636                                 account_chooser_find_account_foreach,
637                                 &data);
638
639         return data.found;
640 }
641
642 static void
643 account_chooser_account_remove_foreach (TpAccount             *account,
644                                         EmpathyAccountChooser *chooser)
645 {
646         GtkListStore *store;
647         GtkComboBox  *combobox;
648         GtkTreeIter   iter;
649
650         combobox = GTK_COMBO_BOX (chooser);
651         store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox));
652
653         if (account_chooser_find_account (chooser, account, &iter)) {
654                 gtk_list_store_remove (store, &iter);
655         }
656 }
657
658 static void
659 account_chooser_update_iter (EmpathyAccountChooser *chooser,
660                              GtkTreeIter           *iter)
661 {
662         EmpathyAccountChooserPriv *priv;
663         GtkListStore              *store;
664         GtkComboBox               *combobox;
665         TpAccount                 *account;
666         const gchar               *icon_name;
667         gboolean                   is_enabled = TRUE;
668
669         priv = GET_PRIV (chooser);
670
671         combobox = GTK_COMBO_BOX (chooser);
672         store = GTK_LIST_STORE (gtk_combo_box_get_model (combobox));
673
674         gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
675                             COL_ACCOUNT_POINTER, &account,
676                             -1);
677
678         icon_name = tp_account_get_icon_name (account);
679         if (priv->filter) {
680                 is_enabled = priv->filter (account, priv->filter_data);
681         }
682
683         gtk_list_store_set (store, iter,
684                             COL_ACCOUNT_IMAGE, icon_name,
685                             COL_ACCOUNT_TEXT, tp_account_get_display_name (account),
686                             COL_ACCOUNT_ENABLED, is_enabled,
687                             -1);
688
689         /* set first connected account as active account */
690         if (priv->account_manually_set == FALSE &&
691             priv->set_active_item == FALSE && is_enabled) {
692                 priv->set_active_item = TRUE;
693                 gtk_combo_box_set_active_iter (combobox, iter);
694         }
695
696         g_object_unref (account);
697 }
698
699 static void
700 account_chooser_status_changed_cb (TpAccount  *account,
701                                    guint       old_status,
702                                    guint       new_status,
703                                    guint       reason,
704                                    gchar      *dbus_error_name,
705                                    GHashTable *details,
706                                    gpointer    user_data)
707 {
708         EmpathyAccountChooser *chooser = user_data;
709         GtkTreeIter iter;
710
711         if (account_chooser_find_account (chooser, account, &iter)) {
712                 account_chooser_update_iter (chooser, &iter);
713         }
714 }
715
716 static gboolean
717 account_chooser_separator_func (GtkTreeModel         *model,
718                                 GtkTreeIter          *iter,
719                                 EmpathyAccountChooser *chooser)
720 {
721         EmpathyAccountChooserPriv *priv;
722         gchar                    *text;
723         gboolean                  is_separator;
724
725         priv = GET_PRIV (chooser);
726
727         if (!priv->has_all_option) {
728                 return FALSE;
729         }
730
731         gtk_tree_model_get (model, iter, COL_ACCOUNT_TEXT, &text, -1);
732         is_separator = text == NULL;
733         g_free (text);
734
735         return is_separator;
736 }
737
738 static gboolean
739 account_chooser_set_account_foreach (GtkTreeModel   *model,
740                                      GtkTreePath    *path,
741                                      GtkTreeIter    *iter,
742                                      SetAccountData *data)
743 {
744         TpAccount *account;
745         gboolean   equal;
746
747         gtk_tree_model_get (model, iter, COL_ACCOUNT_POINTER, &account, -1);
748
749         /* Special case so we can make it possible to select the All option */
750         if ((data->account == NULL) != (account == NULL)) {
751                 equal = FALSE;
752         }
753         else {
754                 equal = (data->account == account);
755         }
756
757         if (account) {
758                 g_object_unref (account);
759         }
760
761         if (equal) {
762                 GtkComboBox *combobox;
763
764                 combobox = GTK_COMBO_BOX (data->chooser);
765                 gtk_combo_box_set_active_iter (combobox, iter);
766
767                 data->set = TRUE;
768         }
769
770         return equal;
771 }
772
773 static gboolean
774 account_chooser_filter_foreach (GtkTreeModel *model,
775                                 GtkTreePath  *path,
776                                 GtkTreeIter  *iter,
777                                 gpointer      chooser)
778 {
779         account_chooser_update_iter (chooser, iter);
780         return FALSE;
781 }
782
783 /**
784  * empathy_account_chooser_set_filter:
785  * @chooser: an #EmpathyAccountChooser
786  * @filter: a filter
787  * @user_data: data to pass to @filter, or %NULL
788  *
789  * Sets a filter on the @chooser so only accounts that are %TRUE in the eyes
790  * of the filter are visible in the @chooser.
791  */
792 void
793 empathy_account_chooser_set_filter (EmpathyAccountChooser           *chooser,
794                                     EmpathyAccountChooserFilterFunc  filter,
795                                     gpointer                         user_data)
796 {
797         EmpathyAccountChooserPriv *priv;
798         GtkTreeModel *model;
799
800         g_return_if_fail (EMPATHY_IS_ACCOUNT_CHOOSER (chooser));
801
802         priv = GET_PRIV (chooser);
803
804         priv->filter = filter;
805         priv->filter_data = user_data;
806
807         /* Refilter existing data */
808         priv->set_active_item = FALSE;
809         model = gtk_combo_box_get_model (GTK_COMBO_BOX (chooser));
810         gtk_tree_model_foreach (model, account_chooser_filter_foreach, chooser);
811 }
812
813 /**
814  * EmpathyAccountChooserFilterFunc:
815  * @account: a #TpAccount
816  * @user_data: user data, or %NULL
817  *
818  * A function which decides whether the account indicated by @account
819  * is visible.
820  *
821  * Return value: whether the account indicated by @account is visible.
822  */
823
824 /**
825  * empathy_account_chooser_filter_is_connected:
826  * @account: a #TpAccount
827  * @user_data: user data or %NULL
828  *
829  * A useful #EmpathyAccountChooserFilterFunc that one could pass into
830  * empathy_account_chooser_set_filter() and only show connected accounts.
831  *
832  * Return value: Whether @account is connected
833  */
834 gboolean
835 empathy_account_chooser_filter_is_connected (TpAccount *account,
836                                              gpointer   user_data)
837 {
838         return (tp_account_get_connection_status (account, NULL)
839             == TP_CONNECTION_STATUS_CONNECTED);
840 }
841
842 gboolean
843 empathy_account_chooser_is_ready (EmpathyAccountChooser *self)
844 {
845         EmpathyAccountChooserPriv *priv = GET_PRIV (self);
846
847         return priv->ready;
848 }