]> git.0d.be Git - empathy.git/blob - src/empathy-accounts-dialog.c
Consider connected account which doesn't implement presence as available (#604700)
[empathy.git] / src / empathy-accounts-dialog.c
1 /*
2  * Copyright (C) 2005-2007 Imendio AB
3  * Copyright (C) 2007-2009 Collabora Ltd.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA  02110-1301  USA
19  *
20  * Authors: Martyn Russell <martyn@imendio.com>
21  *          Xavier Claessens <xclaesse@gmail.com>
22  *          Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
23  *          Jonathan Tellier <jonathan.tellier@gmail.com>
24  */
25
26 #include <config.h>
27
28 #include <string.h>
29 #include <stdlib.h>
30
31 #include <gtk/gtk.h>
32 #include <glib/gi18n.h>
33 #include <dbus/dbus-glib.h>
34
35 #include <telepathy-glib/account-manager.h>
36 #include <telepathy-glib/util.h>
37
38 #include <libempathy/empathy-utils.h>
39 #include <libempathy/empathy-connection-managers.h>
40 #include <libempathy/empathy-connectivity.h>
41 #include <libempathy-gtk/empathy-ui-utils.h>
42
43 #include <libempathy-gtk/empathy-protocol-chooser.h>
44 #include <libempathy-gtk/empathy-account-widget.h>
45 #include <libempathy-gtk/empathy-account-widget-irc.h>
46 #include <libempathy-gtk/empathy-account-widget-sip.h>
47 #include <libempathy-gtk/empathy-cell-renderer-activatable.h>
48 #include <libempathy-gtk/empathy-conf.h>
49 #include <libempathy-gtk/empathy-images.h>
50
51 #include "empathy-accounts-dialog.h"
52 #include "empathy-import-dialog.h"
53 #include "empathy-import-utils.h"
54 #include "ephy-spinner.h"
55
56 #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
57 #include <libempathy/empathy-debug.h>
58
59 /* Flashing delay for icons (milliseconds). */
60 #define FLASH_TIMEOUT 500
61
62 /* The primary text of the dialog shown to the user when he is about to lose
63  * unsaved changes */
64 #define PENDING_CHANGES_QUESTION_PRIMARY_TEXT \
65   _("There are unsaved modifications to your %s account.")
66 /* The primary text of the dialog shown to the user when he is about to lose
67  * an unsaved new account */
68 #define UNSAVED_NEW_ACCOUNT_QUESTION_PRIMARY_TEXT \
69   _("Your new account has not been saved yet.")
70
71 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccountsDialog)
72 G_DEFINE_TYPE (EmpathyAccountsDialog, empathy_accounts_dialog, GTK_TYPE_DIALOG);
73
74 static EmpathyAccountsDialog *dialog_singleton = NULL;
75
76 typedef struct {
77   GtkWidget *alignment_settings;
78   GtkWidget *alignment_infobar;
79
80   GtkWidget *vbox_details;
81   GtkWidget *infobar;
82   GtkWidget *label_status;
83   GtkWidget *image_status;
84   GtkWidget *throbber;
85   GtkWidget *frame_no_protocol;
86
87   GtkWidget *treeview;
88
89   GtkWidget *button_add;
90   GtkWidget *button_import;
91
92   GtkWidget *combobox_protocol;
93   GtkWidget *hbox_protocol;
94
95   GtkWidget *image_type;
96   GtkWidget *label_name;
97   GtkWidget *label_type;
98   GtkWidget *settings_widget;
99
100   /* We have to keep a reference on the actual EmpathyAccountWidget, not just
101    * his GtkWidget. It is the only reliable source we can query to know if
102    * there are any unsaved changes to the currently selected account. We can't
103    * look at the account settings because it does not contain everything that
104    * can be changed using the EmpathyAccountWidget. For instance, it does not
105    * contain the state of the "Enabled" checkbox. */
106   EmpathyAccountWidget *setting_widget_object;
107
108   gboolean  connecting_show;
109   guint connecting_id;
110
111   gulong  settings_ready_id;
112   EmpathyAccountSettings *settings_ready;
113
114   TpAccountManager *account_manager;
115   EmpathyConnectionManagers *cms;
116
117   GtkWindow *parent_window;
118   TpAccount *initial_selection;
119
120   /* Those are needed when changing the selected row. When a user selects
121    * another account and there are unsaved changes on the currently selected
122    * one, a confirmation message box is presented to him. Since his answer
123    * is retrieved asynchronously, we keep some information as member of the
124    * EmpathyAccountsDialog object. */
125   gboolean force_change_row;
126   GtkTreeRowReference *destination_row;
127
128   gboolean dispose_has_run;
129 } EmpathyAccountsDialogPriv;
130
131 enum {
132   COL_NAME,
133   COL_STATUS,
134   COL_ACCOUNT_POINTER,
135   COL_ACCOUNT_SETTINGS_POINTER,
136   COL_COUNT
137 };
138
139 enum {
140   PROP_PARENT = 1
141 };
142
143 static EmpathyAccountSettings * accounts_dialog_model_get_selected_settings (
144     EmpathyAccountsDialog *dialog);
145
146 static gboolean accounts_dialog_get_settings_iter (
147     EmpathyAccountsDialog *dialog,
148     EmpathyAccountSettings *settings,
149     GtkTreeIter *iter);
150
151 static void accounts_dialog_model_select_first (EmpathyAccountsDialog *dialog);
152
153 static void accounts_dialog_update_settings (EmpathyAccountsDialog *dialog,
154     EmpathyAccountSettings *settings);
155
156 static void accounts_dialog_add (EmpathyAccountsDialog *dialog,
157     EmpathyAccountSettings *settings);
158
159 static void accounts_dialog_model_set_selected (EmpathyAccountsDialog *dialog,
160     EmpathyAccountSettings *settings);
161
162 static void accounts_dialog_connection_changed_cb (TpAccount *account,
163     guint old_status,
164     guint current,
165     guint reason,
166     gchar *dbus_error_name,
167     GHashTable *details,
168     EmpathyAccountsDialog *dialog);
169
170 static void accounts_dialog_presence_changed_cb (TpAccount *account,
171     guint presence,
172     gchar *status,
173     gchar *status_message,
174     EmpathyAccountsDialog *dialog);
175
176 static void
177 accounts_dialog_update_name_label (EmpathyAccountsDialog *dialog,
178     const gchar *display_name)
179 {
180   gchar *text;
181   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
182
183   text = g_markup_printf_escaped ("<big><b>%s</b></big>", display_name);
184   gtk_label_set_markup (GTK_LABEL (priv->label_name), text);
185
186   g_free (text);
187 }
188
189 static void
190 accounts_dialog_update_status_infobar (EmpathyAccountsDialog *dialog,
191     TpAccount *account)
192 {
193   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
194   const gchar               *message;
195   gchar                     *status_message = NULL;
196   guint                     status;
197   guint                     reason;
198   guint                     presence;
199   EmpathyConnectivity       *connectivity;
200   GtkTreeView               *view;
201   GtkTreeModel              *model;
202   GtkTreeSelection          *selection;
203   GtkTreeIter               iter;
204   TpAccount                 *selected_account;
205   gboolean                  account_enabled;
206   gboolean                  creating_account;
207
208   view = GTK_TREE_VIEW (priv->treeview);
209   selection = gtk_tree_view_get_selection (view);
210
211   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
212     return;
213
214   gtk_tree_model_get (model, &iter,
215       COL_ACCOUNT_POINTER, &selected_account, -1);
216
217   /* do not update the infobar when the account is not selected */
218   if (account != selected_account)
219     return;
220
221   if (account != NULL)
222     {
223       status = tp_account_get_connection_status (account, &reason);
224       presence = tp_account_get_current_presence (account, NULL, &status_message);
225       account_enabled = tp_account_is_enabled (account);
226       creating_account = FALSE;
227
228       if (status == TP_CONNECTION_STATUS_CONNECTED &&
229           (presence == TP_CONNECTION_PRESENCE_TYPE_OFFLINE ||
230            presence == TP_CONNECTION_PRESENCE_TYPE_UNSET))
231         /* If presence is Unset (CM doesn't implement SimplePresence) but we
232          * are connected, consider ourself as Available.
233          * We also check Offline because of this MC5 bug: fd.o #26060 */
234         presence = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
235     }
236   else
237     {
238       status = TP_CONNECTION_STATUS_DISCONNECTED;
239       presence = TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
240       account_enabled = FALSE;
241       creating_account = TRUE;
242     }
243
244   gtk_image_set_from_icon_name (GTK_IMAGE (priv->image_status),
245       empathy_icon_name_for_presence (presence), GTK_ICON_SIZE_SMALL_TOOLBAR);
246
247   if (account_enabled)
248     {
249       switch (status)
250         {
251           case TP_CONNECTION_STATUS_CONNECTING:
252             message = _("Connecting…");
253             gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
254                 GTK_MESSAGE_INFO);
255
256             ephy_spinner_start (EPHY_SPINNER (priv->throbber));
257             gtk_widget_show (priv->throbber);
258             gtk_widget_hide (priv->image_status);
259             break;
260           case TP_CONNECTION_STATUS_CONNECTED:
261             if (g_strcmp0 (status_message, "") == 0)
262               {
263                 message = g_strdup_printf ("%s",
264                     empathy_presence_get_default_message (presence));
265               }
266             else
267               {
268                 message = g_strdup_printf ("%s â€” %s",
269                     empathy_presence_get_default_message (presence),
270                     status_message);
271               }
272             gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
273                 GTK_MESSAGE_INFO);
274
275             gtk_widget_show (priv->image_status);
276             gtk_widget_hide (priv->throbber);
277             break;
278           case TP_CONNECTION_STATUS_DISCONNECTED:
279             message = g_strdup_printf (_("Disconnected â€” %s"),
280                 empathy_status_reason_get_default_message (reason));
281
282             if (reason == TP_CONNECTION_STATUS_REASON_REQUESTED)
283               {
284                 message = g_strdup_printf (_("Offline â€” %s"),
285                     empathy_status_reason_get_default_message (reason));
286                 gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
287                     GTK_MESSAGE_WARNING);
288               }
289             else
290               {
291                 gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
292                     GTK_MESSAGE_ERROR);
293               }
294
295             connectivity = empathy_connectivity_dup_singleton ();
296             if (!empathy_connectivity_is_online (connectivity))
297                message = _("Offline â€” No Network Connection");
298
299             g_object_unref (connectivity);
300             ephy_spinner_stop (EPHY_SPINNER (priv->throbber));
301             gtk_widget_show (priv->image_status);
302             gtk_widget_hide (priv->throbber);
303             break;
304           default:
305             message = _("Unknown Status");
306             gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
307                 GTK_MESSAGE_WARNING);
308
309             ephy_spinner_stop (EPHY_SPINNER (priv->throbber));
310             gtk_widget_hide (priv->image_status);
311             gtk_widget_hide (priv->throbber);
312         }
313     }
314   else
315     {
316       message = _("Offline â€” Account Disabled");
317
318       gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
319           GTK_MESSAGE_WARNING);
320       ephy_spinner_stop (EPHY_SPINNER (priv->throbber));
321       gtk_widget_show (priv->image_status);
322       gtk_widget_hide (priv->throbber);
323     }
324
325   gtk_label_set_text (GTK_LABEL (priv->label_status), message);
326   gtk_widget_show (priv->label_status);
327
328   if (!creating_account)
329     gtk_widget_show (priv->infobar);
330   else
331     gtk_widget_hide (priv->infobar);
332
333   g_free (status_message);
334 }
335
336 static void
337 empathy_account_dialog_widget_cancelled_cb (
338     EmpathyAccountWidget *widget_object,
339     EmpathyAccountsDialog *dialog)
340 {
341   GtkTreeView *view;
342   GtkTreeModel *model;
343   GtkTreeSelection *selection;
344   GtkTreeIter iter;
345   EmpathyAccountSettings *settings;
346   TpAccount *account;
347   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
348
349   view = GTK_TREE_VIEW (priv->treeview);
350   selection = gtk_tree_view_get_selection (view);
351
352   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
353     return;
354
355   gtk_tree_model_get (model, &iter,
356       COL_ACCOUNT_SETTINGS_POINTER, &settings,
357       COL_ACCOUNT_POINTER, &account, -1);
358
359   empathy_account_widget_discard_pending_changes (priv->setting_widget_object);
360
361   if (account == NULL)
362     {
363       /* We were creating an account. We remove the selected row */
364       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
365     }
366   else
367     {
368       /* We were modifying an account. We discard the changes by reloading the
369        * settings and the UI. */
370       accounts_dialog_update_settings (dialog, settings);
371       g_object_unref (account);
372     }
373
374   gtk_widget_set_sensitive (priv->treeview, TRUE);
375   gtk_widget_set_sensitive (priv->button_add, TRUE);
376   gtk_widget_set_sensitive (priv->button_import, TRUE);
377
378   if (settings != NULL)
379     g_object_unref (settings);
380 }
381
382 static void
383 empathy_account_dialog_account_created_cb (EmpathyAccountWidget *widget_object,
384     TpAccount *account,
385     EmpathyAccountsDialog *dialog)
386 {
387   gchar *display_name;
388   EmpathyAccountSettings *settings =
389       accounts_dialog_model_get_selected_settings (dialog);
390   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
391
392   display_name = empathy_account_widget_get_default_display_name (
393       widget_object);
394
395   empathy_account_settings_set_display_name_async (settings,
396       display_name, NULL, NULL);
397
398   g_free (display_name);
399
400   accounts_dialog_update_settings (dialog, settings);
401   accounts_dialog_update_status_infobar (dialog,
402       empathy_account_settings_get_account (settings));
403
404   gtk_widget_set_sensitive (priv->treeview, TRUE);
405   gtk_widget_set_sensitive (priv->button_add, TRUE);
406   gtk_widget_set_sensitive (priv->button_import, TRUE);
407
408   empathy_signal_connect_weak (account, "status-changed",
409       G_CALLBACK (accounts_dialog_connection_changed_cb), G_OBJECT (dialog));
410   empathy_signal_connect_weak (account, "presence-changed",
411       G_CALLBACK (accounts_dialog_presence_changed_cb), G_OBJECT (dialog));
412
413   if (settings)
414     g_object_unref (settings);
415 }
416
417 static void
418 account_dialog_create_settings_widget (EmpathyAccountsDialog *dialog,
419     EmpathyAccountSettings *settings)
420 {
421   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
422   gchar                     *icon_name;
423   TpAccount                 *account;
424
425   priv->setting_widget_object =
426       empathy_account_widget_new_for_protocol (settings, FALSE);
427
428   priv->settings_widget =
429       empathy_account_widget_get_widget (priv->setting_widget_object);
430
431   g_signal_connect (priv->setting_widget_object, "account-created",
432         G_CALLBACK (empathy_account_dialog_account_created_cb), dialog);
433   g_signal_connect (priv->setting_widget_object, "cancelled",
434           G_CALLBACK (empathy_account_dialog_widget_cancelled_cb), dialog);
435
436   gtk_container_add (GTK_CONTAINER (priv->alignment_settings),
437       priv->settings_widget);
438   gtk_widget_show (priv->settings_widget);
439
440   icon_name = empathy_account_settings_get_icon_name (settings);
441
442   if (!gtk_icon_theme_has_icon (gtk_icon_theme_get_default (),
443           icon_name))
444     /* show the default icon; keep this in sync with the default
445      * one in empathy-accounts-dialog.ui.
446      */
447     icon_name = GTK_STOCK_CUT;
448
449   gtk_image_set_from_icon_name (GTK_IMAGE (priv->image_type),
450       icon_name, GTK_ICON_SIZE_DIALOG);
451   gtk_widget_set_tooltip_text (priv->image_type,
452       empathy_protocol_name_to_display_name
453       (empathy_account_settings_get_protocol (settings)));
454   gtk_widget_show (priv->image_type);
455
456   accounts_dialog_update_name_label (dialog,
457       empathy_account_settings_get_display_name (settings));
458
459   account = empathy_account_settings_get_account (settings);
460   accounts_dialog_update_status_infobar (dialog, account);
461 }
462
463 static void
464 account_dialog_settings_ready_cb (EmpathyAccountSettings *settings,
465     GParamSpec *spec,
466     EmpathyAccountsDialog *dialog)
467 {
468   if (empathy_account_settings_is_ready (settings))
469     account_dialog_create_settings_widget (dialog, settings);
470 }
471
472 static void
473 accounts_dialog_model_select_first (EmpathyAccountsDialog *dialog)
474 {
475   GtkTreeView      *view;
476   GtkTreeModel     *model;
477   GtkTreeSelection *selection;
478   GtkTreeIter       iter;
479   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
480
481   /* select first */
482   view = GTK_TREE_VIEW (priv->treeview);
483   model = gtk_tree_view_get_model (view);
484
485   if (gtk_tree_model_get_iter_first (model, &iter))
486     {
487       selection = gtk_tree_view_get_selection (view);
488       gtk_tree_selection_select_iter (selection, &iter);
489     }
490   else
491     {
492       accounts_dialog_update_settings (dialog, NULL);
493     }
494 }
495
496 static gboolean
497 accounts_dialog_has_pending_change (EmpathyAccountsDialog *dialog,
498     TpAccount **account)
499 {
500   GtkTreeIter iter;
501   GtkTreeModel *model;
502   GtkTreeSelection *selection;
503   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
504
505   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
506
507   if (gtk_tree_selection_get_selected (selection, &model, &iter))
508     gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, account, -1);
509
510   return priv->setting_widget_object != NULL
511       && empathy_account_widget_contains_pending_changes (
512           priv->setting_widget_object);
513 }
514
515 static void
516 accounts_dialog_setup_ui_to_add_account (EmpathyAccountsDialog *dialog)
517 {
518   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
519   EmpathyAccountSettings *settings;
520   gchar *str;
521   const gchar *display_name;
522   TpConnectionManager *cm;
523   TpConnectionManagerProtocol *proto;
524   gboolean is_gtalk;
525
526   cm = empathy_protocol_chooser_dup_selected (
527       EMPATHY_PROTOCOL_CHOOSER (priv->combobox_protocol), &proto, &is_gtalk);
528   if (cm == NULL)
529     return;
530
531   display_name = empathy_protocol_name_to_display_name (
532       is_gtalk ? "gtalk" : proto->name);
533
534   if (display_name == NULL)
535     display_name = proto->name;
536
537   /* Create account */
538   /* To translator: %s is the name of the protocol, such as "Google Talk" or
539    * "Yahoo!"
540    */
541   str = g_strdup_printf (_("New %s account"), display_name);
542   settings = empathy_account_settings_new (cm->name, proto->name, str);
543
544   g_free (str);
545
546   if (is_gtalk)
547     empathy_account_settings_set_icon_name_async (settings, "im-google-talk",
548         NULL, NULL);
549
550   accounts_dialog_add (dialog, settings);
551   accounts_dialog_model_set_selected (dialog, settings);
552
553   gtk_widget_show_all (priv->hbox_protocol);
554
555   g_object_unref (settings);
556   g_object_unref (cm);
557 }
558
559 static void
560 accounts_dialog_protocol_changed_cb (GtkWidget *widget,
561     EmpathyAccountsDialog *dialog)
562 {
563   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
564   GtkTreeSelection *selection;
565   GtkTreeModel *model;
566   GtkTreeIter iter;
567   gboolean creating;
568
569   /* The "changed" signal is fired during the initiation of the
570    * EmpathyProtocolChooser while populating the widget. Such signals should
571    * be ignored so we check if we are actually creating a new account. */
572   if (priv->setting_widget_object == NULL)
573     return;
574
575   g_object_get (priv->setting_widget_object,
576       "creating-account", &creating, NULL);
577   if (!creating)
578     return;
579
580   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
581
582   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
583     return;
584
585   /* We are creating a new widget to replace the current one, don't ask
586    * confirmation to the user. */
587   priv->force_change_row = TRUE;
588   gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
589
590   accounts_dialog_setup_ui_to_add_account (dialog);
591 }
592
593 static void
594 accounts_dialog_show_question_dialog (EmpathyAccountsDialog *dialog,
595     const gchar *primary_text,
596     const gchar *secondary_text,
597     GCallback response_callback,
598     gpointer user_data,
599     const gchar *first_button_text,
600     ...)
601 {
602   va_list button_args;
603   GtkWidget *message_dialog;
604   const gchar *button_text;
605
606   message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
607       GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
608       GTK_MESSAGE_QUESTION,
609       GTK_BUTTONS_NONE,
610       "%s", primary_text);
611
612   gtk_message_dialog_format_secondary_text (
613       GTK_MESSAGE_DIALOG (message_dialog), "%s", secondary_text);
614
615   va_start (button_args, first_button_text);
616   for (button_text = first_button_text;
617        button_text;
618        button_text = va_arg (button_args, const gchar *))
619     {
620       gint response_id;
621       response_id = va_arg (button_args, gint);
622
623       gtk_dialog_add_button (GTK_DIALOG (message_dialog), button_text,
624           response_id);
625     }
626   va_end (button_args);
627
628   g_signal_connect (message_dialog, "response", response_callback, user_data);
629
630   gtk_widget_show (message_dialog);
631 }
632
633 static void
634 accounts_dialog_add_pending_changes_response_cb (GtkDialog *message_dialog,
635   gint response_id,
636   gpointer *user_data)
637 {
638   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
639   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
640
641   gtk_widget_destroy (GTK_WIDGET (message_dialog));
642
643   if (response_id == GTK_RESPONSE_YES)
644     {
645       empathy_account_widget_discard_pending_changes (
646           priv->setting_widget_object);
647       accounts_dialog_setup_ui_to_add_account (dialog);
648     }
649 }
650
651 static gchar *
652 get_dialog_primary_text (TpAccount *account)
653 {
654   if (account != NULL)
655     {
656       /* Existing account */
657       return g_strdup_printf (PENDING_CHANGES_QUESTION_PRIMARY_TEXT,
658           tp_account_get_display_name (account));
659     }
660   else
661     {
662       /* Newly created account */
663       return g_strdup (UNSAVED_NEW_ACCOUNT_QUESTION_PRIMARY_TEXT);
664     }
665 }
666
667 static void
668 accounts_dialog_button_add_clicked_cb (GtkWidget *button,
669     EmpathyAccountsDialog *dialog)
670 {
671   TpAccount *account = NULL;
672   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
673
674   if (accounts_dialog_has_pending_change (dialog, &account))
675     {
676       gchar *question_dialog_primary_text = get_dialog_primary_text (account);
677
678       accounts_dialog_show_question_dialog (dialog,
679           question_dialog_primary_text,
680           _("You are about to create a new account, which will discard\n"
681               "your changes. Are you sure you want to proceed?"),
682           G_CALLBACK (accounts_dialog_add_pending_changes_response_cb),
683           dialog,
684           GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
685           GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
686
687       g_free (question_dialog_primary_text);
688     }
689   else
690     {
691       accounts_dialog_setup_ui_to_add_account (dialog);
692       gtk_widget_set_sensitive (priv->treeview, FALSE);
693       gtk_widget_set_sensitive (priv->button_add, FALSE);
694       gtk_widget_set_sensitive (priv->button_import, FALSE);
695     }
696 }
697
698 static void
699 accounts_dialog_update_settings (EmpathyAccountsDialog *dialog,
700     EmpathyAccountSettings *settings)
701 {
702   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
703
704   if (priv->settings_ready != NULL)
705     {
706       g_signal_handler_disconnect (priv->settings_ready,
707           priv->settings_ready_id);
708       priv->settings_ready = NULL;
709       priv->settings_ready_id = 0;
710     }
711
712   if (!settings)
713     {
714       GtkTreeView  *view;
715       GtkTreeModel *model;
716       GtkTreeSelection *selection;
717
718       view = GTK_TREE_VIEW (priv->treeview);
719       model = gtk_tree_view_get_model (view);
720       selection = gtk_tree_view_get_selection (view);
721
722       if (gtk_tree_model_iter_n_children (model, NULL) > 0)
723         {
724           /* We have configured accounts, select the first one if there
725            * is no other account selected already. */
726           if (!gtk_tree_selection_get_selected (selection, NULL, NULL))
727             accounts_dialog_model_select_first (dialog);
728
729           return;
730         }
731       if (empathy_connection_managers_get_cms_num (priv->cms) > 0)
732         {
733           /* We have no account configured but we have some
734            * profiles installed. The user obviously wants to add
735            * an account. Click on the Add button for him. */
736           accounts_dialog_button_add_clicked_cb (priv->button_add,
737               dialog);
738           return;
739         }
740
741       /* No account and no profile, warn the user */
742       gtk_widget_hide (priv->vbox_details);
743       gtk_widget_show (priv->frame_no_protocol);
744       gtk_widget_set_sensitive (priv->button_add, FALSE);
745       return;
746     }
747
748   /* We have an account selected, destroy old settings and create a new
749    * one for the account selected */
750   gtk_widget_hide (priv->frame_no_protocol);
751   gtk_widget_show (priv->vbox_details);
752   gtk_widget_hide (priv->hbox_protocol);
753
754   if (priv->settings_widget)
755     {
756       gtk_widget_destroy (priv->settings_widget);
757       priv->settings_widget = NULL;
758     }
759
760   if (empathy_account_settings_is_ready (settings))
761     {
762       account_dialog_create_settings_widget (dialog, settings);
763     }
764   else
765     {
766       priv->settings_ready = settings;
767       priv->settings_ready_id =
768         g_signal_connect (settings, "notify::ready",
769             G_CALLBACK (account_dialog_settings_ready_cb), dialog);
770     }
771
772 }
773
774 static void
775 accounts_dialog_name_editing_started_cb (GtkCellRenderer *renderer,
776     GtkCellEditable *editable,
777     gchar *path,
778     EmpathyAccountsDialog *dialog)
779 {
780   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
781
782   if (priv->connecting_id)
783     g_source_remove (priv->connecting_id);
784
785   DEBUG ("Editing account name started; stopping flashing");
786 }
787
788 static void
789 accounts_dialog_model_pixbuf_data_func (GtkTreeViewColumn *tree_column,
790     GtkCellRenderer *cell,
791     GtkTreeModel *model,
792     GtkTreeIter *iter,
793     EmpathyAccountsDialog *dialog)
794 {
795   EmpathyAccountSettings  *settings;
796   gchar              *icon_name;
797   GdkPixbuf          *pixbuf;
798   TpConnectionStatus  status;
799   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
800
801   gtk_tree_model_get (model, iter,
802       COL_STATUS, &status,
803       COL_ACCOUNT_SETTINGS_POINTER, &settings,
804       -1);
805
806   icon_name = empathy_account_settings_get_icon_name (settings);
807   pixbuf = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON);
808
809   if (pixbuf)
810     {
811       if (status == TP_CONNECTION_STATUS_DISCONNECTED ||
812           (status == TP_CONNECTION_STATUS_CONNECTING &&
813               !priv->connecting_show))
814         {
815           GdkPixbuf *modded_pixbuf;
816
817           modded_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
818               TRUE,
819               8,
820               gdk_pixbuf_get_width (pixbuf),
821               gdk_pixbuf_get_height (pixbuf));
822
823           gdk_pixbuf_saturate_and_pixelate (pixbuf,
824               modded_pixbuf,
825               1.0,
826               TRUE);
827           g_object_unref (pixbuf);
828           pixbuf = modded_pixbuf;
829         }
830     }
831
832   g_object_set (cell,
833       "visible", TRUE,
834       "pixbuf", pixbuf,
835       NULL);
836
837   g_object_unref (settings);
838
839   if (pixbuf)
840     g_object_unref (pixbuf);
841 }
842
843 static gboolean
844 accounts_dialog_row_changed_foreach (GtkTreeModel *model,
845     GtkTreePath *path,
846     GtkTreeIter *iter,
847     gpointer user_data)
848 {
849   gtk_tree_model_row_changed (model, path, iter);
850
851   return FALSE;
852 }
853
854 static gboolean
855 accounts_dialog_flash_connecting_cb (EmpathyAccountsDialog *dialog)
856 {
857   GtkTreeView  *view;
858   GtkTreeModel *model;
859   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
860
861   priv->connecting_show = !priv->connecting_show;
862
863   view = GTK_TREE_VIEW (priv->treeview);
864   model = gtk_tree_view_get_model (view);
865
866   gtk_tree_model_foreach (model, accounts_dialog_row_changed_foreach, NULL);
867
868   return TRUE;
869 }
870
871 static void
872 accounts_dialog_name_edited_cb (GtkCellRendererText *renderer,
873     gchar *path,
874     gchar *new_text,
875     EmpathyAccountsDialog *dialog)
876 {
877   EmpathyAccountSettings    *settings;
878   GtkTreeModel *model;
879   GtkTreePath  *treepath;
880   GtkTreeIter   iter;
881   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
882   gboolean connecting;
883
884   empathy_account_manager_get_accounts_connected (&connecting);
885
886   if (connecting)
887     {
888       priv->connecting_id = g_timeout_add (FLASH_TIMEOUT,
889           (GSourceFunc) accounts_dialog_flash_connecting_cb,
890           dialog);
891     }
892
893   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
894   treepath = gtk_tree_path_new_from_string (path);
895   gtk_tree_model_get_iter (model, &iter, treepath);
896   gtk_tree_model_get (model, &iter,
897       COL_ACCOUNT_SETTINGS_POINTER, &settings,
898       -1);
899   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
900       COL_NAME, new_text,
901       -1);
902   gtk_tree_path_free (treepath);
903
904   empathy_account_settings_set_display_name_async (settings, new_text,
905       NULL, NULL);
906   g_object_set (settings, "display-name-overridden", TRUE, NULL);
907   g_object_unref (settings);
908 }
909
910 static void
911 accounts_dialog_delete_account_response_cb (GtkDialog *message_dialog,
912   gint response_id,
913   gpointer user_data)
914 {
915   TpAccount *account;
916   GtkTreeModel *model;
917   GtkTreeIter iter;
918   GtkTreeSelection *selection;
919   EmpathyAccountsDialog *account_dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
920   EmpathyAccountsDialogPriv *priv = GET_PRIV (account_dialog);
921
922   if (response_id == GTK_RESPONSE_YES)
923     {
924       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
925
926       if (!gtk_tree_selection_get_selected (selection, &model, &iter))
927         return;
928
929       gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, &account, -1);
930
931       if (account != NULL)
932         {
933           tp_account_remove_async (account, NULL, NULL);
934           g_object_unref (account);
935           account = NULL;
936         }
937
938       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
939       accounts_dialog_model_select_first (account_dialog);
940     }
941
942   gtk_widget_destroy (GTK_WIDGET (message_dialog));
943 }
944
945 static void
946 accounts_dialog_view_delete_activated_cb (EmpathyCellRendererActivatable *cell,
947     const gchar *path_string,
948     EmpathyAccountsDialog *dialog)
949 {
950   TpAccount *account;
951   GtkTreeModel *model;
952   GtkTreeIter iter;
953   gchar *question_dialog_primary_text;
954   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
955
956   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
957
958   if (!gtk_tree_model_get_iter_from_string (model, &iter, path_string))
959     return;
960
961   gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, &account, -1);
962
963   if (account == NULL || !tp_account_is_valid (account))
964     {
965       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
966       accounts_dialog_model_select_first (dialog);
967       return;
968     }
969
970   question_dialog_primary_text = g_strdup_printf (
971       _("Do you want to remove %s from your computer?"),
972       tp_account_get_display_name (account));
973
974   accounts_dialog_show_question_dialog (dialog, question_dialog_primary_text,
975       _("This will not remove your account on the server."),
976       G_CALLBACK (accounts_dialog_delete_account_response_cb),
977       dialog,
978       GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
979       GTK_STOCK_REMOVE, GTK_RESPONSE_YES, NULL);
980
981   g_free (question_dialog_primary_text);
982
983   if (account != NULL)
984     {
985       g_object_unref (account);
986       account = NULL;
987     }
988 }
989
990 static void
991 accounts_dialog_model_add_columns (EmpathyAccountsDialog *dialog)
992 {
993   GtkTreeView       *view;
994   GtkTreeViewColumn *column;
995   GtkCellRenderer   *cell;
996   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
997
998   view = GTK_TREE_VIEW (priv->treeview);
999   gtk_tree_view_set_headers_visible (view, FALSE);
1000
1001   /* Account column */
1002   column = gtk_tree_view_column_new ();
1003   gtk_tree_view_column_set_expand (column, TRUE);
1004   gtk_tree_view_append_column (view, column);
1005
1006   /* Icon renderer */
1007   cell = gtk_cell_renderer_pixbuf_new ();
1008   gtk_tree_view_column_pack_start (column, cell, FALSE);
1009   gtk_tree_view_column_set_cell_data_func (column, cell,
1010       (GtkTreeCellDataFunc)
1011       accounts_dialog_model_pixbuf_data_func,
1012       dialog,
1013       NULL);
1014
1015   /* Name renderer */
1016   cell = gtk_cell_renderer_text_new ();
1017   g_object_set (cell,
1018       "ellipsize", PANGO_ELLIPSIZE_END,
1019       "width-chars", 25,
1020       "editable", TRUE,
1021       NULL);
1022   gtk_tree_view_column_pack_start (column, cell, TRUE);
1023   gtk_tree_view_column_add_attribute (column, cell, "text", COL_NAME);
1024   g_signal_connect (cell, "edited",
1025       G_CALLBACK (accounts_dialog_name_edited_cb),
1026       dialog);
1027   g_signal_connect (cell, "editing-started",
1028       G_CALLBACK (accounts_dialog_name_editing_started_cb),
1029       dialog);
1030   g_object_set (cell, "ypad", 4, NULL);
1031
1032   /* Delete column */
1033   cell = empathy_cell_renderer_activatable_new ();
1034   gtk_tree_view_column_pack_start (column, cell, FALSE);
1035   g_object_set (cell,
1036         "icon-name", GTK_STOCK_DELETE,
1037         "show-on-select", TRUE,
1038         NULL);
1039
1040   g_signal_connect (cell, "path-activated",
1041       G_CALLBACK (accounts_dialog_view_delete_activated_cb),
1042       dialog);
1043 }
1044
1045 static EmpathyAccountSettings *
1046 accounts_dialog_model_get_selected_settings (EmpathyAccountsDialog *dialog)
1047 {
1048   GtkTreeView      *view;
1049   GtkTreeModel     *model;
1050   GtkTreeSelection *selection;
1051   GtkTreeIter       iter;
1052   EmpathyAccountSettings   *settings;
1053   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1054
1055   view = GTK_TREE_VIEW (priv->treeview);
1056   selection = gtk_tree_view_get_selection (view);
1057
1058   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1059     return NULL;
1060
1061   gtk_tree_model_get (model, &iter,
1062       COL_ACCOUNT_SETTINGS_POINTER, &settings, -1);
1063
1064   return settings;
1065 }
1066
1067 static void
1068 accounts_dialog_model_selection_changed (GtkTreeSelection *selection,
1069     EmpathyAccountsDialog *dialog)
1070 {
1071   EmpathyAccountSettings *settings;
1072   GtkTreeModel *model;
1073   GtkTreeIter   iter;
1074   gboolean      is_selection;
1075
1076   is_selection = gtk_tree_selection_get_selected (selection, &model, &iter);
1077
1078   settings = accounts_dialog_model_get_selected_settings (dialog);
1079   accounts_dialog_update_settings (dialog, settings);
1080
1081   if (settings != NULL)
1082     g_object_unref (settings);
1083 }
1084
1085 static void
1086 accounts_dialog_selection_change_response_cb (GtkDialog *message_dialog,
1087   gint response_id,
1088   gpointer *user_data)
1089 {
1090   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1091   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1092
1093   gtk_widget_destroy (GTK_WIDGET (message_dialog));
1094
1095     if (response_id == GTK_RESPONSE_YES && priv->destination_row != NULL)
1096       {
1097         /* The user wants to lose unsaved changes to the currently selected
1098          * account and select another account. We discard the changes and
1099          * select the other account. */
1100         GtkTreePath *path;
1101         GtkTreeSelection *selection;
1102
1103         priv->force_change_row = TRUE;
1104         empathy_account_widget_discard_pending_changes (
1105             priv->setting_widget_object);
1106
1107         path = gtk_tree_row_reference_get_path (priv->destination_row);
1108         selection = gtk_tree_view_get_selection (
1109             GTK_TREE_VIEW (priv->treeview));
1110
1111         if (path != NULL)
1112           {
1113             /* This will trigger a call to
1114              * accounts_dialog_account_selection_change() */
1115             gtk_tree_selection_select_path (selection, path);
1116             gtk_tree_path_free (path);
1117           }
1118
1119         gtk_tree_row_reference_free (priv->destination_row);
1120       }
1121     else
1122       {
1123         priv->force_change_row = FALSE;
1124       }
1125 }
1126
1127 static gboolean
1128 accounts_dialog_account_selection_change (GtkTreeSelection *selection,
1129     GtkTreeModel *model,
1130     GtkTreePath *path,
1131     gboolean path_currently_selected,
1132     gpointer data)
1133 {
1134   TpAccount *account = NULL;
1135   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (data);
1136   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1137
1138   if (priv->force_change_row)
1139     {
1140       /* We came back here because the user wants to discard changes to his
1141        * modified account. The changes have already been discarded so we
1142        * just change the selected row. */
1143       priv->force_change_row = FALSE;
1144       return TRUE;
1145     }
1146
1147   if (accounts_dialog_has_pending_change (dialog, &account))
1148     {
1149       /* The currently selected account has some unsaved changes. We ask
1150        * the user if he really wants to lose his changes and select another
1151        * account */
1152       gchar *question_dialog_primary_text = get_dialog_primary_text (account);
1153       priv->destination_row = gtk_tree_row_reference_new (model, path);
1154
1155       accounts_dialog_show_question_dialog (dialog,
1156           question_dialog_primary_text,
1157           _("You are about to select another account, which will discard\n"
1158               "your changes. Are you sure you want to proceed?"),
1159           G_CALLBACK (accounts_dialog_selection_change_response_cb),
1160           dialog,
1161           GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1162           GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
1163
1164       g_free (question_dialog_primary_text);
1165     }
1166   else
1167     {
1168       return TRUE;
1169     }
1170
1171   return FALSE;
1172 }
1173
1174 static void
1175 accounts_dialog_model_setup (EmpathyAccountsDialog *dialog)
1176 {
1177   GtkListStore     *store;
1178   GtkTreeSelection *selection;
1179   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1180
1181   store = gtk_list_store_new (COL_COUNT,
1182       G_TYPE_STRING,         /* name */
1183       G_TYPE_UINT,           /* status */
1184       TP_TYPE_ACCOUNT,   /* account */
1185       EMPATHY_TYPE_ACCOUNT_SETTINGS); /* settings */
1186
1187   gtk_tree_view_set_model (GTK_TREE_VIEW (priv->treeview),
1188       GTK_TREE_MODEL (store));
1189
1190   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1191   gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
1192   gtk_tree_selection_set_select_function (selection,
1193       accounts_dialog_account_selection_change, dialog, NULL);
1194
1195   g_signal_connect (selection, "changed",
1196       G_CALLBACK (accounts_dialog_model_selection_changed),
1197       dialog);
1198
1199   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
1200       COL_NAME, GTK_SORT_ASCENDING);
1201
1202   accounts_dialog_model_add_columns (dialog);
1203
1204   g_object_unref (store);
1205 }
1206
1207 static gboolean
1208 accounts_dialog_get_settings_iter (EmpathyAccountsDialog *dialog,
1209     EmpathyAccountSettings *settings,
1210     GtkTreeIter *iter)
1211 {
1212   GtkTreeView      *view;
1213   GtkTreeSelection *selection;
1214   GtkTreeModel     *model;
1215   gboolean          ok;
1216   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1217
1218   /* Update the status in the model */
1219   view = GTK_TREE_VIEW (priv->treeview);
1220   selection = gtk_tree_view_get_selection (view);
1221   model = gtk_tree_view_get_model (view);
1222
1223   for (ok = gtk_tree_model_get_iter_first (model, iter);
1224        ok;
1225        ok = gtk_tree_model_iter_next (model, iter))
1226     {
1227       EmpathyAccountSettings *this_settings;
1228       gboolean   equal;
1229
1230       gtk_tree_model_get (model, iter,
1231           COL_ACCOUNT_SETTINGS_POINTER, &this_settings,
1232           -1);
1233
1234       equal = (this_settings == settings);
1235       g_object_unref (this_settings);
1236
1237       if (equal)
1238         return TRUE;
1239     }
1240
1241   return FALSE;
1242 }
1243
1244 static gboolean
1245 accounts_dialog_get_account_iter (EmpathyAccountsDialog *dialog,
1246     TpAccount *account,
1247     GtkTreeIter *iter)
1248 {
1249   GtkTreeView      *view;
1250   GtkTreeSelection *selection;
1251   GtkTreeModel     *model;
1252   gboolean          ok;
1253   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1254
1255   /* Update the status in the model */
1256   view = GTK_TREE_VIEW (priv->treeview);
1257   selection = gtk_tree_view_get_selection (view);
1258   model = gtk_tree_view_get_model (view);
1259
1260   for (ok = gtk_tree_model_get_iter_first (model, iter);
1261        ok;
1262        ok = gtk_tree_model_iter_next (model, iter))
1263     {
1264       EmpathyAccountSettings *settings;
1265       gboolean   equal;
1266
1267       gtk_tree_model_get (model, iter,
1268           COL_ACCOUNT_SETTINGS_POINTER, &settings,
1269           -1);
1270
1271       equal = empathy_account_settings_has_account (settings, account);
1272       g_object_unref (settings);
1273
1274       if (equal)
1275         return TRUE;
1276     }
1277
1278   return FALSE;
1279 }
1280
1281 static void
1282 accounts_dialog_model_set_selected (EmpathyAccountsDialog *dialog,
1283     EmpathyAccountSettings *settings)
1284 {
1285   GtkTreeSelection *selection;
1286   GtkTreeIter       iter;
1287   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1288
1289   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1290   if (accounts_dialog_get_settings_iter (dialog, settings, &iter))
1291     gtk_tree_selection_select_iter (selection, &iter);
1292 }
1293 static void
1294 accounts_dialog_add (EmpathyAccountsDialog *dialog,
1295     EmpathyAccountSettings *settings)
1296 {
1297   GtkTreeModel       *model;
1298   GtkTreeIter         iter;
1299   const gchar        *name;
1300   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1301
1302   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1303   name = empathy_account_settings_get_display_name (settings);
1304
1305   gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1306
1307   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1308       COL_NAME, name,
1309       COL_STATUS, TP_CONNECTION_STATUS_DISCONNECTED,
1310       COL_ACCOUNT_SETTINGS_POINTER, settings,
1311       -1);
1312 }
1313
1314 static void
1315 accounts_dialog_connection_changed_cb (TpAccount *account,
1316     guint old_status,
1317     guint current,
1318     guint reason,
1319     gchar *dbus_error_name,
1320     GHashTable *details,
1321     EmpathyAccountsDialog *dialog)
1322 {
1323   GtkTreeModel *model;
1324   GtkTreeIter   iter;
1325   gboolean      found;
1326   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1327
1328   /* Update the status-infobar in the details view */
1329   accounts_dialog_update_status_infobar (dialog, account);
1330
1331   /* Update the status in the model */
1332   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1333
1334   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1335     {
1336       GtkTreePath *path;
1337
1338       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1339           COL_STATUS, current,
1340           -1);
1341
1342       path = gtk_tree_model_get_path (model, &iter);
1343       gtk_tree_model_row_changed (model, path, &iter);
1344       gtk_tree_path_free (path);
1345     }
1346
1347   empathy_account_manager_get_accounts_connected (&found);
1348
1349   if (!found && priv->connecting_id)
1350     {
1351       g_source_remove (priv->connecting_id);
1352       priv->connecting_id = 0;
1353     }
1354
1355   if (found && !priv->connecting_id)
1356     priv->connecting_id = g_timeout_add (FLASH_TIMEOUT,
1357         (GSourceFunc) accounts_dialog_flash_connecting_cb,
1358         dialog);
1359 }
1360
1361 static void
1362 accounts_dialog_presence_changed_cb (TpAccount *account,
1363     guint presence,
1364     gchar *status,
1365     gchar *status_message,
1366     EmpathyAccountsDialog *dialog)
1367 {
1368   /* Update the status-infobar in the details view */
1369   accounts_dialog_update_status_infobar (dialog, account);
1370 }
1371
1372 static void
1373 accounts_dialog_account_display_name_changed_cb (TpAccount *account,
1374   GParamSpec *pspec,
1375   gpointer user_data)
1376 {
1377   const gchar *display_name;
1378   GtkTreeIter iter;
1379   GtkTreeModel *model;
1380   EmpathyAccountSettings *settings;
1381   TpAccount *selected_account;
1382   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1383   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1384
1385   display_name = tp_account_get_display_name (account);
1386   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1387   settings = accounts_dialog_model_get_selected_settings (dialog);
1388   if (settings == NULL)
1389     return;
1390
1391   selected_account = empathy_account_settings_get_account (settings);
1392
1393   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1394     {
1395       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1396           COL_NAME, display_name,
1397           -1);
1398     }
1399
1400   if (selected_account == account)
1401     accounts_dialog_update_name_label (dialog, display_name);
1402
1403   g_object_unref (settings);
1404 }
1405
1406 static void
1407 accounts_dialog_add_account (EmpathyAccountsDialog *dialog,
1408     TpAccount *account)
1409 {
1410   EmpathyAccountSettings *settings;
1411   GtkTreeModel       *model;
1412   GtkTreeIter         iter;
1413   TpConnectionStatus  status;
1414   const gchar        *name;
1415   gboolean            enabled;
1416   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1417
1418   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1419   status = tp_account_get_connection_status (account, NULL);
1420   name = tp_account_get_display_name (account);
1421   enabled = tp_account_is_enabled (account);
1422
1423   settings = empathy_account_settings_new_for_account (account);
1424
1425   if (!accounts_dialog_get_account_iter (dialog, account, &iter))
1426     gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1427
1428   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1429       COL_NAME, name,
1430       COL_STATUS, status,
1431       COL_ACCOUNT_POINTER, account,
1432       COL_ACCOUNT_SETTINGS_POINTER, settings,
1433       -1);
1434
1435   accounts_dialog_connection_changed_cb (account,
1436       0,
1437       status,
1438       TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED,
1439       NULL,
1440       NULL,
1441       dialog);
1442
1443   empathy_signal_connect_weak (account, "notify::display-name",
1444       G_CALLBACK (accounts_dialog_account_display_name_changed_cb),
1445       G_OBJECT (dialog));
1446
1447   g_object_unref (settings);
1448 }
1449
1450 static void
1451 account_prepare_cb (GObject *source_object,
1452     GAsyncResult *result,
1453     gpointer user_data)
1454 {
1455   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1456   TpAccount *account = TP_ACCOUNT (source_object);
1457   GError *error = NULL;
1458
1459   if (!tp_account_prepare_finish (account, result, &error))
1460     {
1461       DEBUG ("Failed to prepare account: %s", error->message);
1462       g_error_free (error);
1463       return;
1464     }
1465
1466   accounts_dialog_add_account (dialog, account);
1467 }
1468
1469 static void
1470 accounts_dialog_account_validity_changed_cb (TpAccountManager *manager,
1471     TpAccount *account,
1472     gboolean valid,
1473     EmpathyAccountsDialog *dialog)
1474 {
1475   tp_account_prepare_async (account, NULL, account_prepare_cb, dialog);
1476 }
1477
1478 static void
1479 accounts_dialog_account_removed_cb (TpAccountManager *manager,
1480     TpAccount *account,
1481     EmpathyAccountsDialog *dialog)
1482 {
1483   GtkTreeIter iter;
1484   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1485
1486   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1487     {
1488       gtk_list_store_remove (GTK_LIST_STORE (
1489             gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview))), &iter);
1490     }
1491 }
1492
1493 static void
1494 enable_or_disable_account (EmpathyAccountsDialog *dialog,
1495     TpAccount *account,
1496     gboolean enabled)
1497 {
1498   GtkTreeModel *model;
1499   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1500
1501   /* Update the status in the model */
1502   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1503
1504   /* Update the status-infobar in the details view when disabling */
1505   if (!enabled)
1506     accounts_dialog_update_status_infobar (dialog, account);
1507
1508   DEBUG ("Account %s is now %s",
1509       tp_account_get_display_name (account),
1510       enabled ? "enabled" : "disabled");
1511 }
1512
1513 static void
1514 accounts_dialog_account_disabled_cb (TpAccountManager *manager,
1515     TpAccount *account,
1516     EmpathyAccountsDialog *dialog)
1517 {
1518   enable_or_disable_account (dialog, account, FALSE);
1519 }
1520
1521 static void
1522 accounts_dialog_account_enabled_cb (TpAccountManager *manager,
1523     TpAccount *account,
1524     EmpathyAccountsDialog *dialog)
1525 {
1526   enable_or_disable_account (dialog, account, TRUE);
1527 }
1528
1529 static void
1530 accounts_dialog_button_import_clicked_cb (GtkWidget *button,
1531     EmpathyAccountsDialog *dialog)
1532 {
1533   GtkWidget *import_dialog;
1534
1535   import_dialog = empathy_import_dialog_new (GTK_WINDOW (dialog),
1536       FALSE);
1537   gtk_widget_show (import_dialog);
1538 }
1539
1540 static void
1541 accounts_dialog_close_response_cb (GtkDialog *message_dialog,
1542   gint response_id,
1543   gpointer user_data)
1544 {
1545   GtkWidget *account_dialog = GTK_WIDGET (user_data);
1546
1547   gtk_widget_destroy (GTK_WIDGET (message_dialog));
1548
1549   if (response_id == GTK_RESPONSE_YES)
1550     gtk_widget_destroy (account_dialog);
1551 }
1552
1553 static gboolean
1554 accounts_dialog_delete_event_cb (GtkWidget *widget,
1555     GdkEvent *event,
1556     EmpathyAccountsDialog *dialog)
1557 {
1558   /* we maunally handle responses to delete events */
1559   return TRUE;
1560 }
1561
1562 static void
1563 accounts_dialog_set_selected_account (EmpathyAccountsDialog *dialog,
1564     TpAccount *account)
1565 {
1566   GtkTreeSelection *selection;
1567   GtkTreeIter       iter;
1568   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1569
1570   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1571   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1572     gtk_tree_selection_select_iter (selection, &iter);
1573 }
1574
1575 static void
1576 accounts_dialog_cms_ready_cb (EmpathyConnectionManagers *cms,
1577     GParamSpec *pspec,
1578     EmpathyAccountsDialog *dialog)
1579 {
1580   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1581
1582   if (empathy_connection_managers_is_ready (cms))
1583     {
1584       accounts_dialog_update_settings (dialog, NULL);
1585
1586       if (priv->initial_selection != NULL)
1587         {
1588           accounts_dialog_set_selected_account
1589               (dialog, priv->initial_selection);
1590           g_object_unref (priv->initial_selection);
1591           priv->initial_selection = NULL;
1592         }
1593     }
1594 }
1595
1596 static void
1597 accounts_dialog_accounts_setup (EmpathyAccountsDialog *dialog)
1598 {
1599   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1600   GList *accounts, *l;
1601
1602   g_signal_connect (priv->account_manager, "account-validity-changed",
1603       G_CALLBACK (accounts_dialog_account_validity_changed_cb),
1604       dialog);
1605   g_signal_connect (priv->account_manager, "account-removed",
1606       G_CALLBACK (accounts_dialog_account_removed_cb),
1607       dialog);
1608   g_signal_connect (priv->account_manager, "account-enabled",
1609       G_CALLBACK (accounts_dialog_account_enabled_cb),
1610       dialog);
1611   g_signal_connect (priv->account_manager, "account-disabled",
1612       G_CALLBACK (accounts_dialog_account_disabled_cb),
1613       dialog);
1614
1615   /* Add existing accounts */
1616   accounts = tp_account_manager_get_valid_accounts (priv->account_manager);
1617   for (l = accounts; l; l = l->next)
1618     {
1619       accounts_dialog_add_account (dialog, l->data);
1620
1621       empathy_signal_connect_weak (l->data, "status-changed",
1622           G_CALLBACK (accounts_dialog_connection_changed_cb), G_OBJECT (dialog));
1623       empathy_signal_connect_weak (l->data, "presence-changed",
1624           G_CALLBACK (accounts_dialog_presence_changed_cb), G_OBJECT (dialog));
1625     }
1626   g_list_free (accounts);
1627
1628   priv->cms = empathy_connection_managers_dup_singleton ();
1629   if (!empathy_connection_managers_is_ready (priv->cms))
1630     g_signal_connect (priv->cms, "notify::ready",
1631         G_CALLBACK (accounts_dialog_cms_ready_cb), dialog);
1632
1633   accounts_dialog_model_select_first (dialog);
1634 }
1635
1636 static void
1637 accounts_dialog_manager_ready_cb (GObject *source_object,
1638     GAsyncResult *result,
1639     gpointer user_data)
1640 {
1641   TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object);
1642   GError *error = NULL;
1643
1644   if (!tp_account_manager_prepare_finish (manager, result, &error))
1645     {
1646       DEBUG ("Failed to prepare account manager: %s", error->message);
1647       g_error_free (error);
1648       return;
1649     }
1650
1651   accounts_dialog_accounts_setup (user_data);
1652 }
1653
1654 static void
1655 dialog_response_cb (GtkWidget *widget,
1656     gint response_id,
1657     gpointer user_data)
1658 {
1659   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (widget);
1660
1661   if (response_id == GTK_RESPONSE_HELP)
1662     {
1663       empathy_url_show (widget, "ghelp:empathy?accounts-window");
1664     }
1665   else if (response_id == GTK_RESPONSE_CLOSE ||
1666       response_id == GTK_RESPONSE_DELETE_EVENT)
1667     {
1668       TpAccount *account = NULL;
1669
1670       if (accounts_dialog_has_pending_change (dialog, &account))
1671         {
1672           gchar *question_dialog_primary_text = get_dialog_primary_text (
1673               account);
1674
1675           accounts_dialog_show_question_dialog (dialog,
1676               question_dialog_primary_text,
1677               _("You are about to close the window, which will discard\n"
1678                   "your changes. Are you sure you want to proceed?"),
1679               G_CALLBACK (accounts_dialog_close_response_cb),
1680               widget,
1681               GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1682               GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
1683
1684           g_free (question_dialog_primary_text);
1685         }
1686       else
1687         {
1688           gtk_widget_destroy (widget);
1689         }
1690     }
1691 }
1692
1693 static void
1694 accounts_dialog_build_ui (EmpathyAccountsDialog *dialog)
1695 {
1696   GtkWidget *top_hbox;
1697   GtkBuilder                   *gui;
1698   gchar                        *filename;
1699   EmpathyAccountsDialogPriv    *priv = GET_PRIV (dialog);
1700   GtkWidget                    *content_area;
1701   GtkWidget *action_area;
1702
1703   filename = empathy_file_lookup ("empathy-accounts-dialog.ui", "src");
1704
1705   gui = empathy_builder_get_file (filename,
1706       "accounts_dialog_hbox", &top_hbox,
1707       "vbox_details", &priv->vbox_details,
1708       "frame_no_protocol", &priv->frame_no_protocol,
1709       "alignment_settings", &priv->alignment_settings,
1710       "alignment_infobar", &priv->alignment_infobar,
1711       "treeview", &priv->treeview,
1712       "image_type", &priv->image_type,
1713       "label_name", &priv->label_name,
1714       "button_add", &priv->button_add,
1715       "button_import", &priv->button_import,
1716       "hbox_protocol", &priv->hbox_protocol,
1717       NULL);
1718   g_free (filename);
1719
1720   empathy_builder_connect (gui, dialog,
1721       "button_add", "clicked", accounts_dialog_button_add_clicked_cb,
1722       "button_import", "clicked", accounts_dialog_button_import_clicked_cb,
1723       NULL);
1724
1725   content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
1726
1727   gtk_container_add (GTK_CONTAINER (content_area), top_hbox);
1728
1729   g_object_unref (gui);
1730
1731   action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
1732
1733 #ifdef HAVE_MOBLIN
1734   gtk_widget_hide (action_area);
1735 #endif
1736
1737   priv->combobox_protocol = empathy_protocol_chooser_new ();
1738   gtk_box_pack_start (GTK_BOX (priv->hbox_protocol), priv->combobox_protocol,
1739       TRUE, TRUE, 0);
1740   g_signal_connect (priv->combobox_protocol, "changed",
1741       G_CALLBACK (accounts_dialog_protocol_changed_cb),
1742       dialog);
1743
1744   if (priv->parent_window)
1745     gtk_window_set_transient_for (GTK_WINDOW (dialog),
1746         priv->parent_window);
1747
1748   /* set up spinner */
1749   priv->throbber = ephy_spinner_new ();
1750   ephy_spinner_set_size (EPHY_SPINNER (priv->throbber), GTK_ICON_SIZE_SMALL_TOOLBAR);
1751
1752   priv->infobar = gtk_info_bar_new ();
1753   gtk_container_add (GTK_CONTAINER (priv->alignment_infobar),
1754       priv->infobar);
1755   gtk_widget_show (priv->infobar);
1756
1757   priv->image_status = gtk_image_new_from_icon_name (
1758             empathy_icon_name_for_presence (
1759             TP_CONNECTION_PRESENCE_TYPE_OFFLINE), GTK_ICON_SIZE_SMALL_TOOLBAR);
1760
1761   priv->label_status = gtk_label_new (NULL);
1762
1763   content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (priv->infobar));
1764   gtk_box_pack_start (GTK_BOX (content_area), priv->throbber,
1765       FALSE, FALSE, 0);
1766   gtk_box_pack_start (GTK_BOX (content_area), priv->image_status,
1767       FALSE, FALSE, 0);
1768   gtk_container_add (GTK_CONTAINER (content_area), priv->label_status);
1769
1770   /* Tweak the dialog */
1771   gtk_window_set_title (GTK_WINDOW (dialog), _("Accounts"));
1772   gtk_window_set_role (GTK_WINDOW (dialog), "accounts");
1773
1774   gtk_window_set_default_size (GTK_WINDOW (dialog), 640, -1);
1775
1776   gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
1777
1778   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
1779
1780   /* add dialog buttons */
1781   gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), GTK_BUTTONBOX_END);
1782
1783   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1784       GTK_STOCK_HELP, GTK_RESPONSE_HELP,
1785       GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1786       NULL);
1787
1788   g_signal_connect (dialog, "response",
1789       G_CALLBACK (dialog_response_cb), dialog);
1790
1791   g_signal_connect (dialog, "delete-event",
1792       G_CALLBACK (accounts_dialog_delete_event_cb), dialog);
1793 }
1794
1795 static void
1796 do_dispose (GObject *obj)
1797 {
1798   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (obj);
1799   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1800
1801   if (priv->dispose_has_run)
1802     return;
1803
1804   priv->dispose_has_run = TRUE;
1805
1806   /* Disconnect signals */
1807   g_signal_handlers_disconnect_by_func (priv->account_manager,
1808       accounts_dialog_account_validity_changed_cb,
1809       dialog);
1810   g_signal_handlers_disconnect_by_func (priv->account_manager,
1811       accounts_dialog_account_removed_cb,
1812       dialog);
1813   g_signal_handlers_disconnect_by_func (priv->account_manager,
1814       accounts_dialog_account_enabled_cb,
1815       dialog);
1816   g_signal_handlers_disconnect_by_func (priv->account_manager,
1817       accounts_dialog_account_disabled_cb,
1818       dialog);
1819   g_signal_handlers_disconnect_by_func (priv->account_manager,
1820       accounts_dialog_manager_ready_cb,
1821       dialog);
1822
1823   if (priv->connecting_id)
1824     g_source_remove (priv->connecting_id);
1825
1826   if (priv->account_manager != NULL)
1827     {
1828       g_object_unref (priv->account_manager);
1829       priv->account_manager = NULL;
1830     }
1831
1832   if (priv->cms != NULL)
1833     {
1834       g_object_unref (priv->cms);
1835       priv->cms = NULL;
1836     }
1837
1838   if (priv->initial_selection != NULL)
1839     g_object_unref (priv->initial_selection);
1840   priv->initial_selection = NULL;
1841
1842   G_OBJECT_CLASS (empathy_accounts_dialog_parent_class)->dispose (obj);
1843 }
1844
1845 static GObject *
1846 do_constructor (GType type,
1847     guint n_props,
1848     GObjectConstructParam *props)
1849 {
1850   GObject *retval;
1851
1852   if (dialog_singleton)
1853     {
1854       retval = G_OBJECT (dialog_singleton);
1855     }
1856   else
1857     {
1858       retval =
1859         G_OBJECT_CLASS (empathy_accounts_dialog_parent_class)->constructor
1860             (type, n_props, props);
1861
1862       dialog_singleton = EMPATHY_ACCOUNTS_DIALOG (retval);
1863       g_object_add_weak_pointer (retval, (gpointer) &dialog_singleton);
1864     }
1865
1866   return retval;
1867 }
1868
1869 static void
1870 do_get_property (GObject *object,
1871     guint property_id,
1872     GValue *value,
1873     GParamSpec *pspec)
1874 {
1875   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
1876
1877   switch (property_id)
1878     {
1879     case PROP_PARENT:
1880       g_value_set_object (value, priv->parent_window);
1881       break;
1882     default:
1883       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1884     }
1885 }
1886
1887 static void
1888 do_set_property (GObject *object,
1889     guint property_id,
1890     const GValue *value,
1891     GParamSpec *pspec)
1892 {
1893   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
1894
1895   switch (property_id)
1896     {
1897     case PROP_PARENT:
1898       priv->parent_window = g_value_get_object (value);
1899       break;
1900     default:
1901       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1902     }
1903 }
1904
1905 static void
1906 do_constructed (GObject *object)
1907 {
1908   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (object);
1909   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1910   gboolean import_asked;
1911
1912   accounts_dialog_build_ui (dialog);
1913   accounts_dialog_model_setup (dialog);
1914
1915   /* Set up signalling */
1916   priv->account_manager = tp_account_manager_dup ();
1917
1918   tp_account_manager_prepare_async (priv->account_manager, NULL,
1919       accounts_dialog_manager_ready_cb, dialog);
1920
1921   empathy_conf_get_bool (empathy_conf_get (),
1922       EMPATHY_PREFS_IMPORT_ASKED, &import_asked);
1923
1924   if (empathy_import_accounts_to_import ())
1925     {
1926       gtk_widget_show (priv->button_import);
1927
1928       if (!import_asked)
1929         {
1930           GtkWidget *import_dialog;
1931
1932           empathy_conf_set_bool (empathy_conf_get (),
1933               EMPATHY_PREFS_IMPORT_ASKED, TRUE);
1934           import_dialog = empathy_import_dialog_new (GTK_WINDOW (dialog),
1935               FALSE);
1936           gtk_widget_show (import_dialog);
1937         }
1938     }
1939 }
1940
1941 static void
1942 empathy_accounts_dialog_class_init (EmpathyAccountsDialogClass *klass)
1943 {
1944   GObjectClass *oclass = G_OBJECT_CLASS (klass);
1945   GParamSpec *param_spec;
1946
1947   oclass->constructor = do_constructor;
1948   oclass->dispose = do_dispose;
1949   oclass->constructed = do_constructed;
1950   oclass->set_property = do_set_property;
1951   oclass->get_property = do_get_property;
1952
1953   param_spec = g_param_spec_object ("parent",
1954       "parent", "The parent window",
1955       GTK_TYPE_WINDOW,
1956       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
1957   g_object_class_install_property (oclass, PROP_PARENT, param_spec);
1958
1959   g_type_class_add_private (klass, sizeof (EmpathyAccountsDialogPriv));
1960 }
1961
1962 static void
1963 empathy_accounts_dialog_init (EmpathyAccountsDialog *dialog)
1964 {
1965   EmpathyAccountsDialogPriv *priv;
1966
1967   priv = G_TYPE_INSTANCE_GET_PRIVATE ((dialog),
1968       EMPATHY_TYPE_ACCOUNTS_DIALOG,
1969       EmpathyAccountsDialogPriv);
1970   dialog->priv = priv;
1971 }
1972
1973 /* public methods */
1974
1975 GtkWidget *
1976 empathy_accounts_dialog_show (GtkWindow *parent,
1977     TpAccount *selected_account)
1978 {
1979   EmpathyAccountsDialog *dialog;
1980   EmpathyAccountsDialogPriv *priv;
1981
1982   dialog = g_object_new (EMPATHY_TYPE_ACCOUNTS_DIALOG,
1983       "parent", parent, NULL);
1984
1985   priv = GET_PRIV (dialog);
1986
1987   if (selected_account)
1988     {
1989       if (priv->cms != NULL && empathy_connection_managers_is_ready (priv->cms))
1990         accounts_dialog_set_selected_account (dialog, selected_account);
1991       else
1992         /* save the selection to set it later when the cms
1993          * becomes ready.
1994          */
1995         priv->initial_selection = g_object_ref (selected_account);
1996     }
1997
1998   gtk_window_present (GTK_WINDOW (dialog));
1999
2000   return GTK_WIDGET (dialog);
2001 }