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