]> git.0d.be Git - empathy.git/blob - src/empathy-accounts-dialog.c
Stop greysing and blinking the protocol icon
[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   EmpathyAccountSettings *settings;
574   gchar *account = NULL, *password = NULL;
575
576   /* The "changed" signal is fired during the initiation of the
577    * EmpathyProtocolChooser while populating the widget. Such signals should
578    * be ignored so we check if we are actually creating a new account. */
579   if (priv->setting_widget_object == NULL)
580     return;
581
582   g_object_get (priv->setting_widget_object,
583       "creating-account", &creating, NULL);
584   if (!creating)
585     return;
586
587   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
588
589   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
590     return;
591
592   /* Save "account" and "password" parameters */
593   g_object_get (priv->setting_widget_object, "settings", &settings, NULL);
594
595   if (settings != NULL)
596     {
597       account = g_strdup (empathy_account_settings_get_string (settings,
598             "account"));
599       password = g_strdup (empathy_account_settings_get_string (settings,
600             "password"));
601       g_object_unref (settings);
602     }
603
604   /* We are creating a new widget to replace the current one, don't ask
605    * confirmation to the user. */
606   priv->force_change_row = TRUE;
607   gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
608
609   accounts_dialog_setup_ui_to_add_account (dialog);
610
611   /* Restore "account" and "password" parameters in the new widget */
612   if (account != NULL)
613     {
614       empathy_account_widget_set_account_param (priv->setting_widget_object,
615           account);
616       g_free (account);
617     }
618
619   if (password != NULL)
620     {
621       empathy_account_widget_set_password_param (priv->setting_widget_object,
622           password);
623       g_free (password);
624     }
625 }
626
627 static void
628 accounts_dialog_show_question_dialog (EmpathyAccountsDialog *dialog,
629     const gchar *primary_text,
630     const gchar *secondary_text,
631     GCallback response_callback,
632     gpointer user_data,
633     const gchar *first_button_text,
634     ...)
635 {
636   va_list button_args;
637   GtkWidget *message_dialog;
638   const gchar *button_text;
639
640   message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
641       GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
642       GTK_MESSAGE_QUESTION,
643       GTK_BUTTONS_NONE,
644       "%s", primary_text);
645
646   gtk_message_dialog_format_secondary_text (
647       GTK_MESSAGE_DIALOG (message_dialog), "%s", secondary_text);
648
649   va_start (button_args, first_button_text);
650   for (button_text = first_button_text;
651        button_text;
652        button_text = va_arg (button_args, const gchar *))
653     {
654       gint response_id;
655       response_id = va_arg (button_args, gint);
656
657       gtk_dialog_add_button (GTK_DIALOG (message_dialog), button_text,
658           response_id);
659     }
660   va_end (button_args);
661
662   g_signal_connect (message_dialog, "response", response_callback, user_data);
663
664   gtk_widget_show (message_dialog);
665 }
666
667 static void
668 accounts_dialog_add_pending_changes_response_cb (GtkDialog *message_dialog,
669   gint response_id,
670   gpointer *user_data)
671 {
672   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
673   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
674
675   gtk_widget_destroy (GTK_WIDGET (message_dialog));
676
677   if (response_id == GTK_RESPONSE_YES)
678     {
679       empathy_account_widget_discard_pending_changes (
680           priv->setting_widget_object);
681       accounts_dialog_setup_ui_to_add_account (dialog);
682     }
683 }
684
685 static gchar *
686 get_dialog_primary_text (TpAccount *account)
687 {
688   if (account != NULL)
689     {
690       /* Existing account */
691       return g_strdup_printf (PENDING_CHANGES_QUESTION_PRIMARY_TEXT,
692           tp_account_get_display_name (account));
693     }
694   else
695     {
696       /* Newly created account */
697       return g_strdup (UNSAVED_NEW_ACCOUNT_QUESTION_PRIMARY_TEXT);
698     }
699 }
700
701 static void
702 accounts_dialog_button_add_clicked_cb (GtkWidget *button,
703     EmpathyAccountsDialog *dialog)
704 {
705   TpAccount *account = NULL;
706   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
707
708   if (accounts_dialog_has_pending_change (dialog, &account))
709     {
710       gchar *question_dialog_primary_text = get_dialog_primary_text (account);
711
712       accounts_dialog_show_question_dialog (dialog,
713           question_dialog_primary_text,
714           _("You are about to create a new account, which will discard\n"
715               "your changes. Are you sure you want to proceed?"),
716           G_CALLBACK (accounts_dialog_add_pending_changes_response_cb),
717           dialog,
718           GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
719           GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
720
721       g_free (question_dialog_primary_text);
722     }
723   else
724     {
725       accounts_dialog_setup_ui_to_add_account (dialog);
726       gtk_widget_set_sensitive (priv->treeview, FALSE);
727       gtk_widget_set_sensitive (priv->button_add, FALSE);
728       gtk_widget_set_sensitive (priv->button_import, FALSE);
729     }
730 }
731
732 static void
733 accounts_dialog_update_settings (EmpathyAccountsDialog *dialog,
734     EmpathyAccountSettings *settings)
735 {
736   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
737
738   if (priv->settings_ready != NULL)
739     {
740       g_signal_handler_disconnect (priv->settings_ready,
741           priv->settings_ready_id);
742       priv->settings_ready = NULL;
743       priv->settings_ready_id = 0;
744     }
745
746   if (!settings)
747     {
748       GtkTreeView  *view;
749       GtkTreeModel *model;
750       GtkTreeSelection *selection;
751
752       view = GTK_TREE_VIEW (priv->treeview);
753       model = gtk_tree_view_get_model (view);
754       selection = gtk_tree_view_get_selection (view);
755
756       if (gtk_tree_model_iter_n_children (model, NULL) > 0)
757         {
758           /* We have configured accounts, select the first one if there
759            * is no other account selected already. */
760           if (!gtk_tree_selection_get_selected (selection, NULL, NULL))
761             accounts_dialog_model_select_first (dialog);
762
763           return;
764         }
765       if (empathy_connection_managers_get_cms_num (priv->cms) > 0)
766         {
767           /* We have no account configured but we have some
768            * profiles installed. The user obviously wants to add
769            * an account. Click on the Add button for him. */
770           accounts_dialog_button_add_clicked_cb (priv->button_add,
771               dialog);
772           return;
773         }
774
775       /* No account and no profile, warn the user */
776       gtk_widget_hide (priv->vbox_details);
777       gtk_widget_show (priv->frame_no_protocol);
778       gtk_widget_set_sensitive (priv->button_add, FALSE);
779       return;
780     }
781
782   /* We have an account selected, destroy old settings and create a new
783    * one for the account selected */
784   gtk_widget_hide (priv->frame_no_protocol);
785   gtk_widget_show (priv->vbox_details);
786   gtk_widget_hide (priv->hbox_protocol);
787
788   if (priv->settings_widget)
789     {
790       gtk_widget_destroy (priv->settings_widget);
791       priv->settings_widget = NULL;
792     }
793
794   if (empathy_account_settings_is_ready (settings))
795     {
796       account_dialog_create_settings_widget (dialog, settings);
797     }
798   else
799     {
800       priv->settings_ready = settings;
801       priv->settings_ready_id =
802         g_signal_connect (settings, "notify::ready",
803             G_CALLBACK (account_dialog_settings_ready_cb), dialog);
804     }
805
806 }
807
808 static void
809 accounts_dialog_name_editing_started_cb (GtkCellRenderer *renderer,
810     GtkCellEditable *editable,
811     gchar *path,
812     EmpathyAccountsDialog *dialog)
813 {
814   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
815
816   if (priv->connecting_id)
817     g_source_remove (priv->connecting_id);
818
819   DEBUG ("Editing account name started; stopping flashing");
820 }
821
822 static const gchar *
823 get_status_icon_for_account (EmpathyAccountsDialog *self,
824     TpAccount *account)
825 {
826   EmpathyAccountsDialogPriv *priv = GET_PRIV (self);
827   TpConnectionStatus status;
828   TpConnectionStatusReason reason;
829   TpConnectionPresenceType presence;
830
831   if (account == NULL)
832     return empathy_icon_name_for_presence (TP_CONNECTION_PRESENCE_TYPE_OFFLINE);
833
834   if (!tp_account_is_enabled (account))
835     return empathy_icon_name_for_presence (TP_CONNECTION_PRESENCE_TYPE_OFFLINE);
836
837   status = tp_account_get_connection_status (account, &reason);
838
839   if (status == TP_CONNECTION_STATUS_DISCONNECTED)
840     {
841       if (reason != TP_CONNECTION_STATUS_REASON_REQUESTED)
842         /* An error occured */
843         return GTK_STOCK_DIALOG_ERROR;
844
845         presence = TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
846     }
847   else if (status == TP_CONNECTION_STATUS_CONNECTING)
848     {
849       /* Account is connecting. Display a blinking account alternating between
850        * the offline icon and the requested presence. */
851       if (priv->connecting_show)
852         presence = tp_account_get_requested_presence (account, NULL, NULL);
853       else
854         presence = TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
855     }
856   else
857     {
858       /* status == TP_CONNECTION_STATUS_CONNECTED */
859       presence = tp_account_get_current_presence (account, NULL, NULL);
860
861       /* If presence is Unset (CM doesn't implement SimplePresence),
862        * display the 'available' icon.
863        * We also check Offline because of this MC5 bug: fd.o #26060 */
864       if (presence == TP_CONNECTION_PRESENCE_TYPE_OFFLINE ||
865           presence == TP_CONNECTION_PRESENCE_TYPE_UNSET)
866         presence = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
867     }
868
869   return empathy_icon_name_for_presence (presence);
870 }
871
872 static void
873 accounts_dialog_model_status_pixbuf_data_func (GtkTreeViewColumn *tree_column,
874     GtkCellRenderer *cell,
875     GtkTreeModel *model,
876     GtkTreeIter *iter,
877     EmpathyAccountsDialog *dialog)
878 {
879   TpAccount *account;
880
881   gtk_tree_model_get (model, iter, COL_ACCOUNT_POINTER, &account, -1);
882
883   g_object_set (cell,
884       "icon-name", get_status_icon_for_account (dialog, account),
885       NULL);
886
887   if (account != NULL)
888     g_object_unref (account);
889 }
890
891 static void
892 accounts_dialog_model_protocol_pixbuf_data_func (GtkTreeViewColumn *tree_column,
893     GtkCellRenderer *cell,
894     GtkTreeModel *model,
895     GtkTreeIter *iter,
896     EmpathyAccountsDialog *dialog)
897 {
898   EmpathyAccountSettings  *settings;
899   gchar              *icon_name;
900   GdkPixbuf          *pixbuf;
901   TpConnectionStatus  status;
902
903   gtk_tree_model_get (model, iter,
904       COL_STATUS, &status,
905       COL_ACCOUNT_SETTINGS_POINTER, &settings,
906       -1);
907
908   icon_name = empathy_account_settings_get_icon_name (settings);
909   pixbuf = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON);
910
911   g_object_set (cell,
912       "visible", TRUE,
913       "pixbuf", pixbuf,
914       NULL);
915
916   g_object_unref (settings);
917
918   if (pixbuf)
919     g_object_unref (pixbuf);
920 }
921
922 static gboolean
923 accounts_dialog_row_changed_foreach (GtkTreeModel *model,
924     GtkTreePath *path,
925     GtkTreeIter *iter,
926     gpointer user_data)
927 {
928   gtk_tree_model_row_changed (model, path, iter);
929
930   return FALSE;
931 }
932
933 static gboolean
934 accounts_dialog_flash_connecting_cb (EmpathyAccountsDialog *dialog)
935 {
936   GtkTreeView  *view;
937   GtkTreeModel *model;
938   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
939
940   priv->connecting_show = !priv->connecting_show;
941
942   view = GTK_TREE_VIEW (priv->treeview);
943   model = gtk_tree_view_get_model (view);
944
945   gtk_tree_model_foreach (model, accounts_dialog_row_changed_foreach, NULL);
946
947   return TRUE;
948 }
949
950 static void
951 accounts_dialog_name_edited_cb (GtkCellRendererText *renderer,
952     gchar *path,
953     gchar *new_text,
954     EmpathyAccountsDialog *dialog)
955 {
956   EmpathyAccountSettings    *settings;
957   GtkTreeModel *model;
958   GtkTreePath  *treepath;
959   GtkTreeIter   iter;
960   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
961   gboolean connecting;
962
963   empathy_account_manager_get_accounts_connected (&connecting);
964
965   if (connecting)
966     {
967       priv->connecting_id = g_timeout_add (FLASH_TIMEOUT,
968           (GSourceFunc) accounts_dialog_flash_connecting_cb,
969           dialog);
970     }
971
972   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
973   treepath = gtk_tree_path_new_from_string (path);
974   gtk_tree_model_get_iter (model, &iter, treepath);
975   gtk_tree_model_get (model, &iter,
976       COL_ACCOUNT_SETTINGS_POINTER, &settings,
977       -1);
978   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
979       COL_NAME, new_text,
980       -1);
981   gtk_tree_path_free (treepath);
982
983   empathy_account_settings_set_display_name_async (settings, new_text,
984       NULL, NULL);
985   g_object_set (settings, "display-name-overridden", TRUE, NULL);
986   g_object_unref (settings);
987 }
988
989 static void
990 accounts_dialog_delete_account_response_cb (GtkDialog *message_dialog,
991   gint response_id,
992   gpointer user_data)
993 {
994   TpAccount *account;
995   GtkTreeModel *model;
996   GtkTreeIter iter;
997   GtkTreeSelection *selection;
998   EmpathyAccountsDialog *account_dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
999   EmpathyAccountsDialogPriv *priv = GET_PRIV (account_dialog);
1000
1001   if (response_id == GTK_RESPONSE_YES)
1002     {
1003       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1004
1005       if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1006         return;
1007
1008       gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, &account, -1);
1009
1010       if (account != NULL)
1011         {
1012           tp_account_remove_async (account, NULL, NULL);
1013           g_object_unref (account);
1014           account = NULL;
1015         }
1016
1017       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1018       accounts_dialog_model_select_first (account_dialog);
1019     }
1020
1021   gtk_widget_destroy (GTK_WIDGET (message_dialog));
1022 }
1023
1024 static void
1025 accounts_dialog_view_delete_activated_cb (EmpathyCellRendererActivatable *cell,
1026     const gchar *path_string,
1027     EmpathyAccountsDialog *dialog)
1028 {
1029   TpAccount *account;
1030   GtkTreeModel *model;
1031   GtkTreeIter iter;
1032   gchar *question_dialog_primary_text;
1033   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1034
1035   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1036
1037   if (!gtk_tree_model_get_iter_from_string (model, &iter, path_string))
1038     return;
1039
1040   gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, &account, -1);
1041
1042   if (account == NULL || !tp_account_is_valid (account))
1043     {
1044       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1045       accounts_dialog_model_select_first (dialog);
1046       return;
1047     }
1048
1049   question_dialog_primary_text = g_strdup_printf (
1050       _("Do you want to remove %s from your computer?"),
1051       tp_account_get_display_name (account));
1052
1053   accounts_dialog_show_question_dialog (dialog, question_dialog_primary_text,
1054       _("This will not remove your account on the server."),
1055       G_CALLBACK (accounts_dialog_delete_account_response_cb),
1056       dialog,
1057       GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1058       GTK_STOCK_REMOVE, GTK_RESPONSE_YES, NULL);
1059
1060   g_free (question_dialog_primary_text);
1061
1062   if (account != NULL)
1063     {
1064       g_object_unref (account);
1065       account = NULL;
1066     }
1067 }
1068
1069 static void
1070 accounts_dialog_model_add_columns (EmpathyAccountsDialog *dialog)
1071 {
1072   GtkTreeView       *view;
1073   GtkTreeViewColumn *column;
1074   GtkCellRenderer   *cell;
1075   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1076
1077   view = GTK_TREE_VIEW (priv->treeview);
1078   gtk_tree_view_set_headers_visible (view, FALSE);
1079
1080   /* Account column */
1081   column = gtk_tree_view_column_new ();
1082   gtk_tree_view_column_set_expand (column, TRUE);
1083   gtk_tree_view_append_column (view, column);
1084
1085   /* Status icon renderer */
1086   cell = gtk_cell_renderer_pixbuf_new ();
1087   gtk_tree_view_column_pack_start (column, cell, FALSE);
1088   gtk_tree_view_column_set_cell_data_func (column, cell,
1089       (GtkTreeCellDataFunc)
1090       accounts_dialog_model_status_pixbuf_data_func,
1091       dialog,
1092       NULL);
1093
1094   /* Protocol icon renderer */
1095   cell = gtk_cell_renderer_pixbuf_new ();
1096   gtk_tree_view_column_pack_start (column, cell, FALSE);
1097   gtk_tree_view_column_set_cell_data_func (column, cell,
1098       (GtkTreeCellDataFunc)
1099       accounts_dialog_model_protocol_pixbuf_data_func,
1100       dialog,
1101       NULL);
1102
1103   /* Name renderer */
1104   cell = gtk_cell_renderer_text_new ();
1105   g_object_set (cell,
1106       "ellipsize", PANGO_ELLIPSIZE_END,
1107       "width-chars", 25,
1108       "editable", TRUE,
1109       NULL);
1110   gtk_tree_view_column_pack_start (column, cell, TRUE);
1111   gtk_tree_view_column_add_attribute (column, cell, "text", COL_NAME);
1112   g_signal_connect (cell, "edited",
1113       G_CALLBACK (accounts_dialog_name_edited_cb),
1114       dialog);
1115   g_signal_connect (cell, "editing-started",
1116       G_CALLBACK (accounts_dialog_name_editing_started_cb),
1117       dialog);
1118   g_object_set (cell, "ypad", 4, NULL);
1119
1120   /* Delete column */
1121   cell = empathy_cell_renderer_activatable_new ();
1122   gtk_tree_view_column_pack_start (column, cell, FALSE);
1123   g_object_set (cell,
1124         "icon-name", GTK_STOCK_DELETE,
1125         "show-on-select", TRUE,
1126         NULL);
1127
1128   g_signal_connect (cell, "path-activated",
1129       G_CALLBACK (accounts_dialog_view_delete_activated_cb),
1130       dialog);
1131 }
1132
1133 static EmpathyAccountSettings *
1134 accounts_dialog_model_get_selected_settings (EmpathyAccountsDialog *dialog)
1135 {
1136   GtkTreeView      *view;
1137   GtkTreeModel     *model;
1138   GtkTreeSelection *selection;
1139   GtkTreeIter       iter;
1140   EmpathyAccountSettings   *settings;
1141   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1142
1143   view = GTK_TREE_VIEW (priv->treeview);
1144   selection = gtk_tree_view_get_selection (view);
1145
1146   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1147     return NULL;
1148
1149   gtk_tree_model_get (model, &iter,
1150       COL_ACCOUNT_SETTINGS_POINTER, &settings, -1);
1151
1152   return settings;
1153 }
1154
1155 static void
1156 accounts_dialog_model_selection_changed (GtkTreeSelection *selection,
1157     EmpathyAccountsDialog *dialog)
1158 {
1159   EmpathyAccountSettings *settings;
1160   GtkTreeModel *model;
1161   GtkTreeIter   iter;
1162   gboolean      is_selection;
1163
1164   is_selection = gtk_tree_selection_get_selected (selection, &model, &iter);
1165
1166   settings = accounts_dialog_model_get_selected_settings (dialog);
1167   accounts_dialog_update_settings (dialog, settings);
1168
1169   if (settings != NULL)
1170     g_object_unref (settings);
1171 }
1172
1173 static void
1174 accounts_dialog_selection_change_response_cb (GtkDialog *message_dialog,
1175   gint response_id,
1176   gpointer *user_data)
1177 {
1178   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1179   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1180
1181   gtk_widget_destroy (GTK_WIDGET (message_dialog));
1182
1183     if (response_id == GTK_RESPONSE_YES && priv->destination_row != NULL)
1184       {
1185         /* The user wants to lose unsaved changes to the currently selected
1186          * account and select another account. We discard the changes and
1187          * select the other account. */
1188         GtkTreePath *path;
1189         GtkTreeSelection *selection;
1190
1191         priv->force_change_row = TRUE;
1192         empathy_account_widget_discard_pending_changes (
1193             priv->setting_widget_object);
1194
1195         path = gtk_tree_row_reference_get_path (priv->destination_row);
1196         selection = gtk_tree_view_get_selection (
1197             GTK_TREE_VIEW (priv->treeview));
1198
1199         if (path != NULL)
1200           {
1201             /* This will trigger a call to
1202              * accounts_dialog_account_selection_change() */
1203             gtk_tree_selection_select_path (selection, path);
1204             gtk_tree_path_free (path);
1205           }
1206
1207         gtk_tree_row_reference_free (priv->destination_row);
1208       }
1209     else
1210       {
1211         priv->force_change_row = FALSE;
1212       }
1213 }
1214
1215 static gboolean
1216 accounts_dialog_account_selection_change (GtkTreeSelection *selection,
1217     GtkTreeModel *model,
1218     GtkTreePath *path,
1219     gboolean path_currently_selected,
1220     gpointer data)
1221 {
1222   TpAccount *account = NULL;
1223   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (data);
1224   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1225
1226   if (priv->force_change_row)
1227     {
1228       /* We came back here because the user wants to discard changes to his
1229        * modified account. The changes have already been discarded so we
1230        * just change the selected row. */
1231       priv->force_change_row = FALSE;
1232       return TRUE;
1233     }
1234
1235   if (accounts_dialog_has_pending_change (dialog, &account))
1236     {
1237       /* The currently selected account has some unsaved changes. We ask
1238        * the user if he really wants to lose his changes and select another
1239        * account */
1240       gchar *question_dialog_primary_text = get_dialog_primary_text (account);
1241       priv->destination_row = gtk_tree_row_reference_new (model, path);
1242
1243       accounts_dialog_show_question_dialog (dialog,
1244           question_dialog_primary_text,
1245           _("You are about to select another account, which will discard\n"
1246               "your changes. Are you sure you want to proceed?"),
1247           G_CALLBACK (accounts_dialog_selection_change_response_cb),
1248           dialog,
1249           GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1250           GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
1251
1252       g_free (question_dialog_primary_text);
1253     }
1254   else
1255     {
1256       return TRUE;
1257     }
1258
1259   return FALSE;
1260 }
1261
1262 static void
1263 accounts_dialog_model_setup (EmpathyAccountsDialog *dialog)
1264 {
1265   GtkListStore     *store;
1266   GtkTreeSelection *selection;
1267   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1268
1269   store = gtk_list_store_new (COL_COUNT,
1270       G_TYPE_STRING,         /* name */
1271       G_TYPE_UINT,           /* status */
1272       TP_TYPE_ACCOUNT,   /* account */
1273       EMPATHY_TYPE_ACCOUNT_SETTINGS); /* settings */
1274
1275   gtk_tree_view_set_model (GTK_TREE_VIEW (priv->treeview),
1276       GTK_TREE_MODEL (store));
1277
1278   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1279   gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
1280   gtk_tree_selection_set_select_function (selection,
1281       accounts_dialog_account_selection_change, dialog, NULL);
1282
1283   g_signal_connect (selection, "changed",
1284       G_CALLBACK (accounts_dialog_model_selection_changed),
1285       dialog);
1286
1287   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
1288       COL_NAME, GTK_SORT_ASCENDING);
1289
1290   accounts_dialog_model_add_columns (dialog);
1291
1292   g_object_unref (store);
1293 }
1294
1295 static gboolean
1296 accounts_dialog_get_settings_iter (EmpathyAccountsDialog *dialog,
1297     EmpathyAccountSettings *settings,
1298     GtkTreeIter *iter)
1299 {
1300   GtkTreeView      *view;
1301   GtkTreeSelection *selection;
1302   GtkTreeModel     *model;
1303   gboolean          ok;
1304   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1305
1306   /* Update the status in the model */
1307   view = GTK_TREE_VIEW (priv->treeview);
1308   selection = gtk_tree_view_get_selection (view);
1309   model = gtk_tree_view_get_model (view);
1310
1311   for (ok = gtk_tree_model_get_iter_first (model, iter);
1312        ok;
1313        ok = gtk_tree_model_iter_next (model, iter))
1314     {
1315       EmpathyAccountSettings *this_settings;
1316       gboolean   equal;
1317
1318       gtk_tree_model_get (model, iter,
1319           COL_ACCOUNT_SETTINGS_POINTER, &this_settings,
1320           -1);
1321
1322       equal = (this_settings == settings);
1323       g_object_unref (this_settings);
1324
1325       if (equal)
1326         return TRUE;
1327     }
1328
1329   return FALSE;
1330 }
1331
1332 static gboolean
1333 accounts_dialog_get_account_iter (EmpathyAccountsDialog *dialog,
1334     TpAccount *account,
1335     GtkTreeIter *iter)
1336 {
1337   GtkTreeView      *view;
1338   GtkTreeSelection *selection;
1339   GtkTreeModel     *model;
1340   gboolean          ok;
1341   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1342
1343   /* Update the status in the model */
1344   view = GTK_TREE_VIEW (priv->treeview);
1345   selection = gtk_tree_view_get_selection (view);
1346   model = gtk_tree_view_get_model (view);
1347
1348   for (ok = gtk_tree_model_get_iter_first (model, iter);
1349        ok;
1350        ok = gtk_tree_model_iter_next (model, iter))
1351     {
1352       EmpathyAccountSettings *settings;
1353       gboolean   equal;
1354
1355       gtk_tree_model_get (model, iter,
1356           COL_ACCOUNT_SETTINGS_POINTER, &settings,
1357           -1);
1358
1359       equal = empathy_account_settings_has_account (settings, account);
1360       g_object_unref (settings);
1361
1362       if (equal)
1363         return TRUE;
1364     }
1365
1366   return FALSE;
1367 }
1368
1369 static void
1370 accounts_dialog_model_set_selected (EmpathyAccountsDialog *dialog,
1371     EmpathyAccountSettings *settings)
1372 {
1373   GtkTreeSelection *selection;
1374   GtkTreeIter       iter;
1375   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1376
1377   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1378   if (accounts_dialog_get_settings_iter (dialog, settings, &iter))
1379     gtk_tree_selection_select_iter (selection, &iter);
1380 }
1381 static void
1382 accounts_dialog_add (EmpathyAccountsDialog *dialog,
1383     EmpathyAccountSettings *settings)
1384 {
1385   GtkTreeModel       *model;
1386   GtkTreeIter         iter;
1387   const gchar        *name;
1388   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1389
1390   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1391   name = empathy_account_settings_get_display_name (settings);
1392
1393   gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1394
1395   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1396       COL_NAME, name,
1397       COL_STATUS, TP_CONNECTION_STATUS_DISCONNECTED,
1398       COL_ACCOUNT_SETTINGS_POINTER, settings,
1399       -1);
1400 }
1401
1402 static void
1403 accounts_dialog_connection_changed_cb (TpAccount *account,
1404     guint old_status,
1405     guint current,
1406     guint reason,
1407     gchar *dbus_error_name,
1408     GHashTable *details,
1409     EmpathyAccountsDialog *dialog)
1410 {
1411   GtkTreeModel *model;
1412   GtkTreeIter   iter;
1413   gboolean      found;
1414   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1415
1416   /* Update the status-infobar in the details view */
1417   accounts_dialog_update_status_infobar (dialog, account);
1418
1419   /* Update the status in the model */
1420   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1421
1422   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1423     {
1424       GtkTreePath *path;
1425
1426       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1427           COL_STATUS, current,
1428           -1);
1429
1430       path = gtk_tree_model_get_path (model, &iter);
1431       gtk_tree_model_row_changed (model, path, &iter);
1432       gtk_tree_path_free (path);
1433     }
1434
1435   empathy_account_manager_get_accounts_connected (&found);
1436
1437   if (!found && priv->connecting_id)
1438     {
1439       g_source_remove (priv->connecting_id);
1440       priv->connecting_id = 0;
1441     }
1442
1443   if (found && !priv->connecting_id)
1444     priv->connecting_id = g_timeout_add (FLASH_TIMEOUT,
1445         (GSourceFunc) accounts_dialog_flash_connecting_cb,
1446         dialog);
1447 }
1448
1449 static void
1450 update_account_in_treeview (EmpathyAccountsDialog *self,
1451     TpAccount *account)
1452 {
1453   EmpathyAccountsDialogPriv *priv = GET_PRIV (self);
1454   GtkTreeIter iter;
1455   GtkTreeModel *model;
1456
1457   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1458   if (accounts_dialog_get_account_iter (self, account, &iter))
1459     {
1460       GtkTreePath *path;
1461
1462       path = gtk_tree_model_get_path (model, &iter);
1463       gtk_tree_model_row_changed (model, path, &iter);
1464       gtk_tree_path_free (path);
1465     }
1466 }
1467
1468 static void
1469 accounts_dialog_presence_changed_cb (TpAccount *account,
1470     guint presence,
1471     gchar *status,
1472     gchar *status_message,
1473     EmpathyAccountsDialog *dialog)
1474 {
1475   /* Update the status-infobar in the details view */
1476   accounts_dialog_update_status_infobar (dialog, account);
1477
1478   update_account_in_treeview (dialog, account);
1479 }
1480
1481 static void
1482 accounts_dialog_account_display_name_changed_cb (TpAccount *account,
1483   GParamSpec *pspec,
1484   gpointer user_data)
1485 {
1486   const gchar *display_name;
1487   GtkTreeIter iter;
1488   GtkTreeModel *model;
1489   EmpathyAccountSettings *settings;
1490   TpAccount *selected_account;
1491   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1492   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1493
1494   display_name = tp_account_get_display_name (account);
1495   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1496   settings = accounts_dialog_model_get_selected_settings (dialog);
1497   if (settings == NULL)
1498     return;
1499
1500   selected_account = empathy_account_settings_get_account (settings);
1501
1502   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1503     {
1504       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1505           COL_NAME, display_name,
1506           -1);
1507     }
1508
1509   if (selected_account == account)
1510     accounts_dialog_update_name_label (dialog, display_name);
1511
1512   g_object_unref (settings);
1513 }
1514
1515 static void
1516 accounts_dialog_add_account (EmpathyAccountsDialog *dialog,
1517     TpAccount *account)
1518 {
1519   EmpathyAccountSettings *settings;
1520   GtkTreeModel       *model;
1521   GtkTreeIter         iter;
1522   TpConnectionStatus  status;
1523   const gchar        *name;
1524   gboolean            enabled;
1525   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1526
1527   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1528   status = tp_account_get_connection_status (account, NULL);
1529   name = tp_account_get_display_name (account);
1530   enabled = tp_account_is_enabled (account);
1531
1532   settings = empathy_account_settings_new_for_account (account);
1533
1534   if (!accounts_dialog_get_account_iter (dialog, account, &iter))
1535     gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1536
1537   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1538       COL_NAME, name,
1539       COL_STATUS, status,
1540       COL_ACCOUNT_POINTER, account,
1541       COL_ACCOUNT_SETTINGS_POINTER, settings,
1542       -1);
1543
1544   accounts_dialog_connection_changed_cb (account,
1545       0,
1546       status,
1547       TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED,
1548       NULL,
1549       NULL,
1550       dialog);
1551
1552   empathy_signal_connect_weak (account, "notify::display-name",
1553       G_CALLBACK (accounts_dialog_account_display_name_changed_cb),
1554       G_OBJECT (dialog));
1555
1556   g_object_unref (settings);
1557 }
1558
1559 static void
1560 account_prepare_cb (GObject *source_object,
1561     GAsyncResult *result,
1562     gpointer user_data)
1563 {
1564   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1565   TpAccount *account = TP_ACCOUNT (source_object);
1566   GError *error = NULL;
1567
1568   if (!tp_account_prepare_finish (account, result, &error))
1569     {
1570       DEBUG ("Failed to prepare account: %s", error->message);
1571       g_error_free (error);
1572       return;
1573     }
1574
1575   accounts_dialog_add_account (dialog, account);
1576 }
1577
1578 static void
1579 accounts_dialog_account_validity_changed_cb (TpAccountManager *manager,
1580     TpAccount *account,
1581     gboolean valid,
1582     EmpathyAccountsDialog *dialog)
1583 {
1584   tp_account_prepare_async (account, NULL, account_prepare_cb, dialog);
1585 }
1586
1587 static void
1588 accounts_dialog_account_removed_cb (TpAccountManager *manager,
1589     TpAccount *account,
1590     EmpathyAccountsDialog *dialog)
1591 {
1592   GtkTreeIter iter;
1593   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1594
1595   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1596     {
1597       gtk_list_store_remove (GTK_LIST_STORE (
1598             gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview))), &iter);
1599     }
1600 }
1601
1602 static void
1603 enable_or_disable_account (EmpathyAccountsDialog *dialog,
1604     TpAccount *account,
1605     gboolean enabled)
1606 {
1607   GtkTreeModel *model;
1608   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1609
1610   /* Update the status in the model */
1611   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1612
1613   /* Update the status-infobar in the details view when disabling */
1614   if (!enabled)
1615     accounts_dialog_update_status_infobar (dialog, account);
1616
1617   DEBUG ("Account %s is now %s",
1618       tp_account_get_display_name (account),
1619       enabled ? "enabled" : "disabled");
1620 }
1621
1622 static void
1623 accounts_dialog_account_disabled_cb (TpAccountManager *manager,
1624     TpAccount *account,
1625     EmpathyAccountsDialog *dialog)
1626 {
1627   enable_or_disable_account (dialog, account, FALSE);
1628   update_account_in_treeview (dialog, account);
1629 }
1630
1631 static void
1632 accounts_dialog_account_enabled_cb (TpAccountManager *manager,
1633     TpAccount *account,
1634     EmpathyAccountsDialog *dialog)
1635 {
1636   enable_or_disable_account (dialog, account, TRUE);
1637 }
1638
1639 static void
1640 accounts_dialog_button_import_clicked_cb (GtkWidget *button,
1641     EmpathyAccountsDialog *dialog)
1642 {
1643   GtkWidget *import_dialog;
1644
1645   import_dialog = empathy_import_dialog_new (GTK_WINDOW (dialog),
1646       FALSE);
1647   gtk_widget_show (import_dialog);
1648 }
1649
1650 static void
1651 accounts_dialog_close_response_cb (GtkDialog *message_dialog,
1652   gint response_id,
1653   gpointer user_data)
1654 {
1655   GtkWidget *account_dialog = GTK_WIDGET (user_data);
1656
1657   gtk_widget_destroy (GTK_WIDGET (message_dialog));
1658
1659   if (response_id == GTK_RESPONSE_YES)
1660     gtk_widget_destroy (account_dialog);
1661 }
1662
1663 static gboolean
1664 accounts_dialog_delete_event_cb (GtkWidget *widget,
1665     GdkEvent *event,
1666     EmpathyAccountsDialog *dialog)
1667 {
1668   /* we maunally handle responses to delete events */
1669   return TRUE;
1670 }
1671
1672 static void
1673 accounts_dialog_set_selected_account (EmpathyAccountsDialog *dialog,
1674     TpAccount *account)
1675 {
1676   GtkTreeSelection *selection;
1677   GtkTreeIter       iter;
1678   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1679
1680   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1681   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1682     gtk_tree_selection_select_iter (selection, &iter);
1683 }
1684
1685 static void
1686 accounts_dialog_cms_ready_cb (EmpathyConnectionManagers *cms,
1687     GParamSpec *pspec,
1688     EmpathyAccountsDialog *dialog)
1689 {
1690   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1691
1692   if (empathy_connection_managers_is_ready (cms))
1693     {
1694       accounts_dialog_update_settings (dialog, NULL);
1695
1696       if (priv->initial_selection != NULL)
1697         {
1698           accounts_dialog_set_selected_account
1699               (dialog, priv->initial_selection);
1700           g_object_unref (priv->initial_selection);
1701           priv->initial_selection = NULL;
1702         }
1703     }
1704 }
1705
1706 static void
1707 accounts_dialog_accounts_setup (EmpathyAccountsDialog *dialog)
1708 {
1709   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1710   GList *accounts, *l;
1711
1712   g_signal_connect (priv->account_manager, "account-validity-changed",
1713       G_CALLBACK (accounts_dialog_account_validity_changed_cb),
1714       dialog);
1715   g_signal_connect (priv->account_manager, "account-removed",
1716       G_CALLBACK (accounts_dialog_account_removed_cb),
1717       dialog);
1718   g_signal_connect (priv->account_manager, "account-enabled",
1719       G_CALLBACK (accounts_dialog_account_enabled_cb),
1720       dialog);
1721   g_signal_connect (priv->account_manager, "account-disabled",
1722       G_CALLBACK (accounts_dialog_account_disabled_cb),
1723       dialog);
1724
1725   /* Add existing accounts */
1726   accounts = tp_account_manager_get_valid_accounts (priv->account_manager);
1727   for (l = accounts; l; l = l->next)
1728     {
1729       accounts_dialog_add_account (dialog, l->data);
1730
1731       empathy_signal_connect_weak (l->data, "status-changed",
1732           G_CALLBACK (accounts_dialog_connection_changed_cb), G_OBJECT (dialog));
1733       empathy_signal_connect_weak (l->data, "presence-changed",
1734           G_CALLBACK (accounts_dialog_presence_changed_cb), G_OBJECT (dialog));
1735     }
1736   g_list_free (accounts);
1737
1738   priv->cms = empathy_connection_managers_dup_singleton ();
1739   if (!empathy_connection_managers_is_ready (priv->cms))
1740     g_signal_connect (priv->cms, "notify::ready",
1741         G_CALLBACK (accounts_dialog_cms_ready_cb), dialog);
1742
1743   accounts_dialog_model_select_first (dialog);
1744 }
1745
1746 static void
1747 accounts_dialog_manager_ready_cb (GObject *source_object,
1748     GAsyncResult *result,
1749     gpointer user_data)
1750 {
1751   TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object);
1752   GError *error = NULL;
1753
1754   if (!tp_account_manager_prepare_finish (manager, result, &error))
1755     {
1756       DEBUG ("Failed to prepare account manager: %s", error->message);
1757       g_error_free (error);
1758       return;
1759     }
1760
1761   accounts_dialog_accounts_setup (user_data);
1762 }
1763
1764 static void
1765 dialog_response_cb (GtkWidget *widget,
1766     gint response_id,
1767     gpointer user_data)
1768 {
1769   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (widget);
1770
1771   if (response_id == GTK_RESPONSE_HELP)
1772     {
1773       empathy_url_show (widget, "ghelp:empathy?accounts-window");
1774     }
1775   else if (response_id == GTK_RESPONSE_CLOSE ||
1776       response_id == GTK_RESPONSE_DELETE_EVENT)
1777     {
1778       TpAccount *account = NULL;
1779
1780       if (accounts_dialog_has_pending_change (dialog, &account))
1781         {
1782           gchar *question_dialog_primary_text = get_dialog_primary_text (
1783               account);
1784
1785           accounts_dialog_show_question_dialog (dialog,
1786               question_dialog_primary_text,
1787               _("You are about to close the window, which will discard\n"
1788                   "your changes. Are you sure you want to proceed?"),
1789               G_CALLBACK (accounts_dialog_close_response_cb),
1790               widget,
1791               GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1792               GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
1793
1794           g_free (question_dialog_primary_text);
1795         }
1796       else
1797         {
1798           gtk_widget_destroy (widget);
1799         }
1800     }
1801 }
1802
1803 static void
1804 accounts_dialog_build_ui (EmpathyAccountsDialog *dialog)
1805 {
1806   GtkWidget *top_hbox;
1807   GtkBuilder                   *gui;
1808   gchar                        *filename;
1809   EmpathyAccountsDialogPriv    *priv = GET_PRIV (dialog);
1810   GtkWidget                    *content_area;
1811   GtkWidget *action_area;
1812
1813   filename = empathy_file_lookup ("empathy-accounts-dialog.ui", "src");
1814
1815   gui = empathy_builder_get_file (filename,
1816       "accounts_dialog_hbox", &top_hbox,
1817       "vbox_details", &priv->vbox_details,
1818       "frame_no_protocol", &priv->frame_no_protocol,
1819       "alignment_settings", &priv->alignment_settings,
1820       "alignment_infobar", &priv->alignment_infobar,
1821       "treeview", &priv->treeview,
1822       "image_type", &priv->image_type,
1823       "label_name", &priv->label_name,
1824       "button_add", &priv->button_add,
1825       "button_import", &priv->button_import,
1826       "hbox_protocol", &priv->hbox_protocol,
1827       NULL);
1828   g_free (filename);
1829
1830   empathy_builder_connect (gui, dialog,
1831       "button_add", "clicked", accounts_dialog_button_add_clicked_cb,
1832       "button_import", "clicked", accounts_dialog_button_import_clicked_cb,
1833       NULL);
1834
1835   content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
1836
1837   gtk_container_add (GTK_CONTAINER (content_area), top_hbox);
1838
1839   g_object_unref (gui);
1840
1841   action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
1842
1843 #ifdef HAVE_MOBLIN
1844   gtk_widget_hide (action_area);
1845 #endif
1846
1847   priv->combobox_protocol = empathy_protocol_chooser_new ();
1848   gtk_box_pack_start (GTK_BOX (priv->hbox_protocol), priv->combobox_protocol,
1849       TRUE, TRUE, 0);
1850   g_signal_connect (priv->combobox_protocol, "changed",
1851       G_CALLBACK (accounts_dialog_protocol_changed_cb),
1852       dialog);
1853
1854   if (priv->parent_window)
1855     gtk_window_set_transient_for (GTK_WINDOW (dialog),
1856         priv->parent_window);
1857
1858   /* set up spinner */
1859   priv->throbber = ephy_spinner_new ();
1860   ephy_spinner_set_size (EPHY_SPINNER (priv->throbber), GTK_ICON_SIZE_SMALL_TOOLBAR);
1861
1862   priv->infobar = gtk_info_bar_new ();
1863   gtk_container_add (GTK_CONTAINER (priv->alignment_infobar),
1864       priv->infobar);
1865   gtk_widget_show (priv->infobar);
1866
1867   priv->image_status = gtk_image_new_from_icon_name (
1868             empathy_icon_name_for_presence (
1869             TP_CONNECTION_PRESENCE_TYPE_OFFLINE), GTK_ICON_SIZE_SMALL_TOOLBAR);
1870
1871   priv->label_status = gtk_label_new (NULL);
1872
1873   content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (priv->infobar));
1874   gtk_box_pack_start (GTK_BOX (content_area), priv->throbber,
1875       FALSE, FALSE, 0);
1876   gtk_box_pack_start (GTK_BOX (content_area), priv->image_status,
1877       FALSE, FALSE, 0);
1878   gtk_container_add (GTK_CONTAINER (content_area), priv->label_status);
1879
1880   /* Tweak the dialog */
1881   gtk_window_set_title (GTK_WINDOW (dialog), _("Accounts"));
1882   gtk_window_set_role (GTK_WINDOW (dialog), "accounts");
1883
1884   gtk_window_set_default_size (GTK_WINDOW (dialog), 640, -1);
1885
1886   gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
1887
1888   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
1889
1890   /* add dialog buttons */
1891   gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), GTK_BUTTONBOX_END);
1892
1893   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1894       GTK_STOCK_HELP, GTK_RESPONSE_HELP,
1895       GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1896       NULL);
1897
1898   g_signal_connect (dialog, "response",
1899       G_CALLBACK (dialog_response_cb), dialog);
1900
1901   g_signal_connect (dialog, "delete-event",
1902       G_CALLBACK (accounts_dialog_delete_event_cb), dialog);
1903 }
1904
1905 static void
1906 do_dispose (GObject *obj)
1907 {
1908   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (obj);
1909   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1910
1911   if (priv->dispose_has_run)
1912     return;
1913
1914   priv->dispose_has_run = TRUE;
1915
1916   /* Disconnect signals */
1917   g_signal_handlers_disconnect_by_func (priv->account_manager,
1918       accounts_dialog_account_validity_changed_cb,
1919       dialog);
1920   g_signal_handlers_disconnect_by_func (priv->account_manager,
1921       accounts_dialog_account_removed_cb,
1922       dialog);
1923   g_signal_handlers_disconnect_by_func (priv->account_manager,
1924       accounts_dialog_account_enabled_cb,
1925       dialog);
1926   g_signal_handlers_disconnect_by_func (priv->account_manager,
1927       accounts_dialog_account_disabled_cb,
1928       dialog);
1929   g_signal_handlers_disconnect_by_func (priv->account_manager,
1930       accounts_dialog_manager_ready_cb,
1931       dialog);
1932
1933   if (priv->connecting_id)
1934     g_source_remove (priv->connecting_id);
1935
1936   if (priv->account_manager != NULL)
1937     {
1938       g_object_unref (priv->account_manager);
1939       priv->account_manager = NULL;
1940     }
1941
1942   if (priv->cms != NULL)
1943     {
1944       g_object_unref (priv->cms);
1945       priv->cms = NULL;
1946     }
1947
1948   if (priv->initial_selection != NULL)
1949     g_object_unref (priv->initial_selection);
1950   priv->initial_selection = NULL;
1951
1952   G_OBJECT_CLASS (empathy_accounts_dialog_parent_class)->dispose (obj);
1953 }
1954
1955 static GObject *
1956 do_constructor (GType type,
1957     guint n_props,
1958     GObjectConstructParam *props)
1959 {
1960   GObject *retval;
1961
1962   if (dialog_singleton)
1963     {
1964       retval = G_OBJECT (dialog_singleton);
1965     }
1966   else
1967     {
1968       retval =
1969         G_OBJECT_CLASS (empathy_accounts_dialog_parent_class)->constructor
1970             (type, n_props, props);
1971
1972       dialog_singleton = EMPATHY_ACCOUNTS_DIALOG (retval);
1973       g_object_add_weak_pointer (retval, (gpointer) &dialog_singleton);
1974     }
1975
1976   return retval;
1977 }
1978
1979 static void
1980 do_get_property (GObject *object,
1981     guint property_id,
1982     GValue *value,
1983     GParamSpec *pspec)
1984 {
1985   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
1986
1987   switch (property_id)
1988     {
1989     case PROP_PARENT:
1990       g_value_set_object (value, priv->parent_window);
1991       break;
1992     default:
1993       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1994     }
1995 }
1996
1997 static void
1998 do_set_property (GObject *object,
1999     guint property_id,
2000     const GValue *value,
2001     GParamSpec *pspec)
2002 {
2003   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
2004
2005   switch (property_id)
2006     {
2007     case PROP_PARENT:
2008       priv->parent_window = g_value_get_object (value);
2009       break;
2010     default:
2011       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2012     }
2013 }
2014
2015 static void
2016 do_constructed (GObject *object)
2017 {
2018   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (object);
2019   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2020   gboolean import_asked;
2021
2022   accounts_dialog_build_ui (dialog);
2023   accounts_dialog_model_setup (dialog);
2024
2025   /* Set up signalling */
2026   priv->account_manager = tp_account_manager_dup ();
2027
2028   tp_account_manager_prepare_async (priv->account_manager, NULL,
2029       accounts_dialog_manager_ready_cb, dialog);
2030
2031   empathy_conf_get_bool (empathy_conf_get (),
2032       EMPATHY_PREFS_IMPORT_ASKED, &import_asked);
2033
2034   if (empathy_import_accounts_to_import ())
2035     {
2036       gtk_widget_show (priv->button_import);
2037
2038       if (!import_asked)
2039         {
2040           GtkWidget *import_dialog;
2041
2042           empathy_conf_set_bool (empathy_conf_get (),
2043               EMPATHY_PREFS_IMPORT_ASKED, TRUE);
2044           import_dialog = empathy_import_dialog_new (GTK_WINDOW (dialog),
2045               FALSE);
2046           gtk_widget_show (import_dialog);
2047         }
2048     }
2049 }
2050
2051 static void
2052 empathy_accounts_dialog_class_init (EmpathyAccountsDialogClass *klass)
2053 {
2054   GObjectClass *oclass = G_OBJECT_CLASS (klass);
2055   GParamSpec *param_spec;
2056
2057   oclass->constructor = do_constructor;
2058   oclass->dispose = do_dispose;
2059   oclass->constructed = do_constructed;
2060   oclass->set_property = do_set_property;
2061   oclass->get_property = do_get_property;
2062
2063   param_spec = g_param_spec_object ("parent",
2064       "parent", "The parent window",
2065       GTK_TYPE_WINDOW,
2066       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2067   g_object_class_install_property (oclass, PROP_PARENT, param_spec);
2068
2069   g_type_class_add_private (klass, sizeof (EmpathyAccountsDialogPriv));
2070 }
2071
2072 static void
2073 empathy_accounts_dialog_init (EmpathyAccountsDialog *dialog)
2074 {
2075   EmpathyAccountsDialogPriv *priv;
2076
2077   priv = G_TYPE_INSTANCE_GET_PRIVATE ((dialog),
2078       EMPATHY_TYPE_ACCOUNTS_DIALOG,
2079       EmpathyAccountsDialogPriv);
2080   dialog->priv = priv;
2081 }
2082
2083 /* public methods */
2084
2085 GtkWidget *
2086 empathy_accounts_dialog_show (GtkWindow *parent,
2087     TpAccount *selected_account)
2088 {
2089   EmpathyAccountsDialog *dialog;
2090   EmpathyAccountsDialogPriv *priv;
2091
2092   dialog = g_object_new (EMPATHY_TYPE_ACCOUNTS_DIALOG,
2093       "parent", parent, NULL);
2094
2095   priv = GET_PRIV (dialog);
2096
2097   if (selected_account)
2098     {
2099       if (priv->cms != NULL && empathy_connection_managers_is_ready (priv->cms))
2100         accounts_dialog_set_selected_account (dialog, selected_account);
2101       else
2102         /* save the selection to set it later when the cms
2103          * becomes ready.
2104          */
2105         priv->initial_selection = g_object_ref (selected_account);
2106     }
2107
2108   gtk_window_present (GTK_WINDOW (dialog));
2109
2110   return GTK_WIDGET (dialog);
2111 }