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