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