]> git.0d.be Git - empathy.git/blob - src/empathy-accounts-dialog.c
update account in treeview when it's 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   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   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
903
904   gtk_tree_model_get (model, iter,
905       COL_STATUS, &status,
906       COL_ACCOUNT_SETTINGS_POINTER, &settings,
907       -1);
908
909   icon_name = empathy_account_settings_get_icon_name (settings);
910   pixbuf = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON);
911
912   if (pixbuf)
913     {
914       if (status == TP_CONNECTION_STATUS_DISCONNECTED ||
915           (status == TP_CONNECTION_STATUS_CONNECTING &&
916               !priv->connecting_show))
917         {
918           GdkPixbuf *modded_pixbuf;
919
920           modded_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
921               TRUE,
922               8,
923               gdk_pixbuf_get_width (pixbuf),
924               gdk_pixbuf_get_height (pixbuf));
925
926           gdk_pixbuf_saturate_and_pixelate (pixbuf,
927               modded_pixbuf,
928               1.0,
929               TRUE);
930           g_object_unref (pixbuf);
931           pixbuf = modded_pixbuf;
932         }
933     }
934
935   g_object_set (cell,
936       "visible", TRUE,
937       "pixbuf", pixbuf,
938       NULL);
939
940   g_object_unref (settings);
941
942   if (pixbuf)
943     g_object_unref (pixbuf);
944 }
945
946 static gboolean
947 accounts_dialog_row_changed_foreach (GtkTreeModel *model,
948     GtkTreePath *path,
949     GtkTreeIter *iter,
950     gpointer user_data)
951 {
952   gtk_tree_model_row_changed (model, path, iter);
953
954   return FALSE;
955 }
956
957 static gboolean
958 accounts_dialog_flash_connecting_cb (EmpathyAccountsDialog *dialog)
959 {
960   GtkTreeView  *view;
961   GtkTreeModel *model;
962   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
963
964   priv->connecting_show = !priv->connecting_show;
965
966   view = GTK_TREE_VIEW (priv->treeview);
967   model = gtk_tree_view_get_model (view);
968
969   gtk_tree_model_foreach (model, accounts_dialog_row_changed_foreach, NULL);
970
971   return TRUE;
972 }
973
974 static void
975 accounts_dialog_name_edited_cb (GtkCellRendererText *renderer,
976     gchar *path,
977     gchar *new_text,
978     EmpathyAccountsDialog *dialog)
979 {
980   EmpathyAccountSettings    *settings;
981   GtkTreeModel *model;
982   GtkTreePath  *treepath;
983   GtkTreeIter   iter;
984   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
985   gboolean connecting;
986
987   empathy_account_manager_get_accounts_connected (&connecting);
988
989   if (connecting)
990     {
991       priv->connecting_id = g_timeout_add (FLASH_TIMEOUT,
992           (GSourceFunc) accounts_dialog_flash_connecting_cb,
993           dialog);
994     }
995
996   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
997   treepath = gtk_tree_path_new_from_string (path);
998   gtk_tree_model_get_iter (model, &iter, treepath);
999   gtk_tree_model_get (model, &iter,
1000       COL_ACCOUNT_SETTINGS_POINTER, &settings,
1001       -1);
1002   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1003       COL_NAME, new_text,
1004       -1);
1005   gtk_tree_path_free (treepath);
1006
1007   empathy_account_settings_set_display_name_async (settings, new_text,
1008       NULL, NULL);
1009   g_object_set (settings, "display-name-overridden", TRUE, NULL);
1010   g_object_unref (settings);
1011 }
1012
1013 static void
1014 accounts_dialog_delete_account_response_cb (GtkDialog *message_dialog,
1015   gint response_id,
1016   gpointer user_data)
1017 {
1018   TpAccount *account;
1019   GtkTreeModel *model;
1020   GtkTreeIter iter;
1021   GtkTreeSelection *selection;
1022   EmpathyAccountsDialog *account_dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1023   EmpathyAccountsDialogPriv *priv = GET_PRIV (account_dialog);
1024
1025   if (response_id == GTK_RESPONSE_YES)
1026     {
1027       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1028
1029       if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1030         return;
1031
1032       gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, &account, -1);
1033
1034       if (account != NULL)
1035         {
1036           tp_account_remove_async (account, NULL, NULL);
1037           g_object_unref (account);
1038           account = NULL;
1039         }
1040
1041       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1042       accounts_dialog_model_select_first (account_dialog);
1043     }
1044
1045   gtk_widget_destroy (GTK_WIDGET (message_dialog));
1046 }
1047
1048 static void
1049 accounts_dialog_view_delete_activated_cb (EmpathyCellRendererActivatable *cell,
1050     const gchar *path_string,
1051     EmpathyAccountsDialog *dialog)
1052 {
1053   TpAccount *account;
1054   GtkTreeModel *model;
1055   GtkTreeIter iter;
1056   gchar *question_dialog_primary_text;
1057   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1058
1059   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1060
1061   if (!gtk_tree_model_get_iter_from_string (model, &iter, path_string))
1062     return;
1063
1064   gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, &account, -1);
1065
1066   if (account == NULL || !tp_account_is_valid (account))
1067     {
1068       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1069       accounts_dialog_model_select_first (dialog);
1070       return;
1071     }
1072
1073   question_dialog_primary_text = g_strdup_printf (
1074       _("Do you want to remove %s from your computer?"),
1075       tp_account_get_display_name (account));
1076
1077   accounts_dialog_show_question_dialog (dialog, question_dialog_primary_text,
1078       _("This will not remove your account on the server."),
1079       G_CALLBACK (accounts_dialog_delete_account_response_cb),
1080       dialog,
1081       GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1082       GTK_STOCK_REMOVE, GTK_RESPONSE_YES, NULL);
1083
1084   g_free (question_dialog_primary_text);
1085
1086   if (account != NULL)
1087     {
1088       g_object_unref (account);
1089       account = NULL;
1090     }
1091 }
1092
1093 static void
1094 accounts_dialog_model_add_columns (EmpathyAccountsDialog *dialog)
1095 {
1096   GtkTreeView       *view;
1097   GtkTreeViewColumn *column;
1098   GtkCellRenderer   *cell;
1099   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1100
1101   view = GTK_TREE_VIEW (priv->treeview);
1102   gtk_tree_view_set_headers_visible (view, FALSE);
1103
1104   /* Account column */
1105   column = gtk_tree_view_column_new ();
1106   gtk_tree_view_column_set_expand (column, TRUE);
1107   gtk_tree_view_append_column (view, column);
1108
1109   /* Status icon renderer */
1110   cell = gtk_cell_renderer_pixbuf_new ();
1111   gtk_tree_view_column_pack_start (column, cell, FALSE);
1112   gtk_tree_view_column_set_cell_data_func (column, cell,
1113       (GtkTreeCellDataFunc)
1114       accounts_dialog_model_status_pixbuf_data_func,
1115       dialog,
1116       NULL);
1117
1118   /* Protocol icon renderer */
1119   cell = gtk_cell_renderer_pixbuf_new ();
1120   gtk_tree_view_column_pack_start (column, cell, FALSE);
1121   gtk_tree_view_column_set_cell_data_func (column, cell,
1122       (GtkTreeCellDataFunc)
1123       accounts_dialog_model_protocol_pixbuf_data_func,
1124       dialog,
1125       NULL);
1126
1127   /* Name renderer */
1128   cell = gtk_cell_renderer_text_new ();
1129   g_object_set (cell,
1130       "ellipsize", PANGO_ELLIPSIZE_END,
1131       "width-chars", 25,
1132       "editable", TRUE,
1133       NULL);
1134   gtk_tree_view_column_pack_start (column, cell, TRUE);
1135   gtk_tree_view_column_add_attribute (column, cell, "text", COL_NAME);
1136   g_signal_connect (cell, "edited",
1137       G_CALLBACK (accounts_dialog_name_edited_cb),
1138       dialog);
1139   g_signal_connect (cell, "editing-started",
1140       G_CALLBACK (accounts_dialog_name_editing_started_cb),
1141       dialog);
1142   g_object_set (cell, "ypad", 4, NULL);
1143
1144   /* Delete column */
1145   cell = empathy_cell_renderer_activatable_new ();
1146   gtk_tree_view_column_pack_start (column, cell, FALSE);
1147   g_object_set (cell,
1148         "icon-name", GTK_STOCK_DELETE,
1149         "show-on-select", TRUE,
1150         NULL);
1151
1152   g_signal_connect (cell, "path-activated",
1153       G_CALLBACK (accounts_dialog_view_delete_activated_cb),
1154       dialog);
1155 }
1156
1157 static EmpathyAccountSettings *
1158 accounts_dialog_model_get_selected_settings (EmpathyAccountsDialog *dialog)
1159 {
1160   GtkTreeView      *view;
1161   GtkTreeModel     *model;
1162   GtkTreeSelection *selection;
1163   GtkTreeIter       iter;
1164   EmpathyAccountSettings   *settings;
1165   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1166
1167   view = GTK_TREE_VIEW (priv->treeview);
1168   selection = gtk_tree_view_get_selection (view);
1169
1170   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1171     return NULL;
1172
1173   gtk_tree_model_get (model, &iter,
1174       COL_ACCOUNT_SETTINGS_POINTER, &settings, -1);
1175
1176   return settings;
1177 }
1178
1179 static void
1180 accounts_dialog_model_selection_changed (GtkTreeSelection *selection,
1181     EmpathyAccountsDialog *dialog)
1182 {
1183   EmpathyAccountSettings *settings;
1184   GtkTreeModel *model;
1185   GtkTreeIter   iter;
1186   gboolean      is_selection;
1187
1188   is_selection = gtk_tree_selection_get_selected (selection, &model, &iter);
1189
1190   settings = accounts_dialog_model_get_selected_settings (dialog);
1191   accounts_dialog_update_settings (dialog, settings);
1192
1193   if (settings != NULL)
1194     g_object_unref (settings);
1195 }
1196
1197 static void
1198 accounts_dialog_selection_change_response_cb (GtkDialog *message_dialog,
1199   gint response_id,
1200   gpointer *user_data)
1201 {
1202   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1203   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1204
1205   gtk_widget_destroy (GTK_WIDGET (message_dialog));
1206
1207     if (response_id == GTK_RESPONSE_YES && priv->destination_row != NULL)
1208       {
1209         /* The user wants to lose unsaved changes to the currently selected
1210          * account and select another account. We discard the changes and
1211          * select the other account. */
1212         GtkTreePath *path;
1213         GtkTreeSelection *selection;
1214
1215         priv->force_change_row = TRUE;
1216         empathy_account_widget_discard_pending_changes (
1217             priv->setting_widget_object);
1218
1219         path = gtk_tree_row_reference_get_path (priv->destination_row);
1220         selection = gtk_tree_view_get_selection (
1221             GTK_TREE_VIEW (priv->treeview));
1222
1223         if (path != NULL)
1224           {
1225             /* This will trigger a call to
1226              * accounts_dialog_account_selection_change() */
1227             gtk_tree_selection_select_path (selection, path);
1228             gtk_tree_path_free (path);
1229           }
1230
1231         gtk_tree_row_reference_free (priv->destination_row);
1232       }
1233     else
1234       {
1235         priv->force_change_row = FALSE;
1236       }
1237 }
1238
1239 static gboolean
1240 accounts_dialog_account_selection_change (GtkTreeSelection *selection,
1241     GtkTreeModel *model,
1242     GtkTreePath *path,
1243     gboolean path_currently_selected,
1244     gpointer data)
1245 {
1246   TpAccount *account = NULL;
1247   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (data);
1248   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1249
1250   if (priv->force_change_row)
1251     {
1252       /* We came back here because the user wants to discard changes to his
1253        * modified account. The changes have already been discarded so we
1254        * just change the selected row. */
1255       priv->force_change_row = FALSE;
1256       return TRUE;
1257     }
1258
1259   if (accounts_dialog_has_pending_change (dialog, &account))
1260     {
1261       /* The currently selected account has some unsaved changes. We ask
1262        * the user if he really wants to lose his changes and select another
1263        * account */
1264       gchar *question_dialog_primary_text = get_dialog_primary_text (account);
1265       priv->destination_row = gtk_tree_row_reference_new (model, path);
1266
1267       accounts_dialog_show_question_dialog (dialog,
1268           question_dialog_primary_text,
1269           _("You are about to select another account, which will discard\n"
1270               "your changes. Are you sure you want to proceed?"),
1271           G_CALLBACK (accounts_dialog_selection_change_response_cb),
1272           dialog,
1273           GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1274           GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
1275
1276       g_free (question_dialog_primary_text);
1277     }
1278   else
1279     {
1280       return TRUE;
1281     }
1282
1283   return FALSE;
1284 }
1285
1286 static void
1287 accounts_dialog_model_setup (EmpathyAccountsDialog *dialog)
1288 {
1289   GtkListStore     *store;
1290   GtkTreeSelection *selection;
1291   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1292
1293   store = gtk_list_store_new (COL_COUNT,
1294       G_TYPE_STRING,         /* name */
1295       G_TYPE_UINT,           /* status */
1296       TP_TYPE_ACCOUNT,   /* account */
1297       EMPATHY_TYPE_ACCOUNT_SETTINGS); /* settings */
1298
1299   gtk_tree_view_set_model (GTK_TREE_VIEW (priv->treeview),
1300       GTK_TREE_MODEL (store));
1301
1302   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1303   gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
1304   gtk_tree_selection_set_select_function (selection,
1305       accounts_dialog_account_selection_change, dialog, NULL);
1306
1307   g_signal_connect (selection, "changed",
1308       G_CALLBACK (accounts_dialog_model_selection_changed),
1309       dialog);
1310
1311   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
1312       COL_NAME, GTK_SORT_ASCENDING);
1313
1314   accounts_dialog_model_add_columns (dialog);
1315
1316   g_object_unref (store);
1317 }
1318
1319 static gboolean
1320 accounts_dialog_get_settings_iter (EmpathyAccountsDialog *dialog,
1321     EmpathyAccountSettings *settings,
1322     GtkTreeIter *iter)
1323 {
1324   GtkTreeView      *view;
1325   GtkTreeSelection *selection;
1326   GtkTreeModel     *model;
1327   gboolean          ok;
1328   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1329
1330   /* Update the status in the model */
1331   view = GTK_TREE_VIEW (priv->treeview);
1332   selection = gtk_tree_view_get_selection (view);
1333   model = gtk_tree_view_get_model (view);
1334
1335   for (ok = gtk_tree_model_get_iter_first (model, iter);
1336        ok;
1337        ok = gtk_tree_model_iter_next (model, iter))
1338     {
1339       EmpathyAccountSettings *this_settings;
1340       gboolean   equal;
1341
1342       gtk_tree_model_get (model, iter,
1343           COL_ACCOUNT_SETTINGS_POINTER, &this_settings,
1344           -1);
1345
1346       equal = (this_settings == settings);
1347       g_object_unref (this_settings);
1348
1349       if (equal)
1350         return TRUE;
1351     }
1352
1353   return FALSE;
1354 }
1355
1356 static gboolean
1357 accounts_dialog_get_account_iter (EmpathyAccountsDialog *dialog,
1358     TpAccount *account,
1359     GtkTreeIter *iter)
1360 {
1361   GtkTreeView      *view;
1362   GtkTreeSelection *selection;
1363   GtkTreeModel     *model;
1364   gboolean          ok;
1365   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1366
1367   /* Update the status in the model */
1368   view = GTK_TREE_VIEW (priv->treeview);
1369   selection = gtk_tree_view_get_selection (view);
1370   model = gtk_tree_view_get_model (view);
1371
1372   for (ok = gtk_tree_model_get_iter_first (model, iter);
1373        ok;
1374        ok = gtk_tree_model_iter_next (model, iter))
1375     {
1376       EmpathyAccountSettings *settings;
1377       gboolean   equal;
1378
1379       gtk_tree_model_get (model, iter,
1380           COL_ACCOUNT_SETTINGS_POINTER, &settings,
1381           -1);
1382
1383       equal = empathy_account_settings_has_account (settings, account);
1384       g_object_unref (settings);
1385
1386       if (equal)
1387         return TRUE;
1388     }
1389
1390   return FALSE;
1391 }
1392
1393 static void
1394 accounts_dialog_model_set_selected (EmpathyAccountsDialog *dialog,
1395     EmpathyAccountSettings *settings)
1396 {
1397   GtkTreeSelection *selection;
1398   GtkTreeIter       iter;
1399   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1400
1401   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1402   if (accounts_dialog_get_settings_iter (dialog, settings, &iter))
1403     gtk_tree_selection_select_iter (selection, &iter);
1404 }
1405 static void
1406 accounts_dialog_add (EmpathyAccountsDialog *dialog,
1407     EmpathyAccountSettings *settings)
1408 {
1409   GtkTreeModel       *model;
1410   GtkTreeIter         iter;
1411   const gchar        *name;
1412   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1413
1414   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1415   name = empathy_account_settings_get_display_name (settings);
1416
1417   gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1418
1419   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1420       COL_NAME, name,
1421       COL_STATUS, TP_CONNECTION_STATUS_DISCONNECTED,
1422       COL_ACCOUNT_SETTINGS_POINTER, settings,
1423       -1);
1424 }
1425
1426 static void
1427 accounts_dialog_connection_changed_cb (TpAccount *account,
1428     guint old_status,
1429     guint current,
1430     guint reason,
1431     gchar *dbus_error_name,
1432     GHashTable *details,
1433     EmpathyAccountsDialog *dialog)
1434 {
1435   GtkTreeModel *model;
1436   GtkTreeIter   iter;
1437   gboolean      found;
1438   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1439
1440   /* Update the status-infobar in the details view */
1441   accounts_dialog_update_status_infobar (dialog, account);
1442
1443   /* Update the status in the model */
1444   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1445
1446   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1447     {
1448       GtkTreePath *path;
1449
1450       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1451           COL_STATUS, current,
1452           -1);
1453
1454       path = gtk_tree_model_get_path (model, &iter);
1455       gtk_tree_model_row_changed (model, path, &iter);
1456       gtk_tree_path_free (path);
1457     }
1458
1459   empathy_account_manager_get_accounts_connected (&found);
1460
1461   if (!found && priv->connecting_id)
1462     {
1463       g_source_remove (priv->connecting_id);
1464       priv->connecting_id = 0;
1465     }
1466
1467   if (found && !priv->connecting_id)
1468     priv->connecting_id = g_timeout_add (FLASH_TIMEOUT,
1469         (GSourceFunc) accounts_dialog_flash_connecting_cb,
1470         dialog);
1471 }
1472
1473 static void
1474 update_account_in_treeview (EmpathyAccountsDialog *self,
1475     TpAccount *account)
1476 {
1477   EmpathyAccountsDialogPriv *priv = GET_PRIV (self);
1478   GtkTreeIter iter;
1479   GtkTreeModel *model;
1480
1481   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1482   if (accounts_dialog_get_account_iter (self, account, &iter))
1483     {
1484       GtkTreePath *path;
1485
1486       path = gtk_tree_model_get_path (model, &iter);
1487       gtk_tree_model_row_changed (model, path, &iter);
1488       gtk_tree_path_free (path);
1489     }
1490 }
1491
1492 static void
1493 accounts_dialog_presence_changed_cb (TpAccount *account,
1494     guint presence,
1495     gchar *status,
1496     gchar *status_message,
1497     EmpathyAccountsDialog *dialog)
1498 {
1499   /* Update the status-infobar in the details view */
1500   accounts_dialog_update_status_infobar (dialog, account);
1501
1502   update_account_in_treeview (dialog, account);
1503 }
1504
1505 static void
1506 accounts_dialog_account_display_name_changed_cb (TpAccount *account,
1507   GParamSpec *pspec,
1508   gpointer user_data)
1509 {
1510   const gchar *display_name;
1511   GtkTreeIter iter;
1512   GtkTreeModel *model;
1513   EmpathyAccountSettings *settings;
1514   TpAccount *selected_account;
1515   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1516   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1517
1518   display_name = tp_account_get_display_name (account);
1519   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1520   settings = accounts_dialog_model_get_selected_settings (dialog);
1521   if (settings == NULL)
1522     return;
1523
1524   selected_account = empathy_account_settings_get_account (settings);
1525
1526   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1527     {
1528       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1529           COL_NAME, display_name,
1530           -1);
1531     }
1532
1533   if (selected_account == account)
1534     accounts_dialog_update_name_label (dialog, display_name);
1535
1536   g_object_unref (settings);
1537 }
1538
1539 static void
1540 accounts_dialog_add_account (EmpathyAccountsDialog *dialog,
1541     TpAccount *account)
1542 {
1543   EmpathyAccountSettings *settings;
1544   GtkTreeModel       *model;
1545   GtkTreeIter         iter;
1546   TpConnectionStatus  status;
1547   const gchar        *name;
1548   gboolean            enabled;
1549   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1550
1551   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1552   status = tp_account_get_connection_status (account, NULL);
1553   name = tp_account_get_display_name (account);
1554   enabled = tp_account_is_enabled (account);
1555
1556   settings = empathy_account_settings_new_for_account (account);
1557
1558   if (!accounts_dialog_get_account_iter (dialog, account, &iter))
1559     gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1560
1561   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1562       COL_NAME, name,
1563       COL_STATUS, status,
1564       COL_ACCOUNT_POINTER, account,
1565       COL_ACCOUNT_SETTINGS_POINTER, settings,
1566       -1);
1567
1568   accounts_dialog_connection_changed_cb (account,
1569       0,
1570       status,
1571       TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED,
1572       NULL,
1573       NULL,
1574       dialog);
1575
1576   empathy_signal_connect_weak (account, "notify::display-name",
1577       G_CALLBACK (accounts_dialog_account_display_name_changed_cb),
1578       G_OBJECT (dialog));
1579
1580   g_object_unref (settings);
1581 }
1582
1583 static void
1584 account_prepare_cb (GObject *source_object,
1585     GAsyncResult *result,
1586     gpointer user_data)
1587 {
1588   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1589   TpAccount *account = TP_ACCOUNT (source_object);
1590   GError *error = NULL;
1591
1592   if (!tp_account_prepare_finish (account, result, &error))
1593     {
1594       DEBUG ("Failed to prepare account: %s", error->message);
1595       g_error_free (error);
1596       return;
1597     }
1598
1599   accounts_dialog_add_account (dialog, account);
1600 }
1601
1602 static void
1603 accounts_dialog_account_validity_changed_cb (TpAccountManager *manager,
1604     TpAccount *account,
1605     gboolean valid,
1606     EmpathyAccountsDialog *dialog)
1607 {
1608   tp_account_prepare_async (account, NULL, account_prepare_cb, dialog);
1609 }
1610
1611 static void
1612 accounts_dialog_account_removed_cb (TpAccountManager *manager,
1613     TpAccount *account,
1614     EmpathyAccountsDialog *dialog)
1615 {
1616   GtkTreeIter iter;
1617   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1618
1619   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1620     {
1621       gtk_list_store_remove (GTK_LIST_STORE (
1622             gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview))), &iter);
1623     }
1624 }
1625
1626 static void
1627 enable_or_disable_account (EmpathyAccountsDialog *dialog,
1628     TpAccount *account,
1629     gboolean enabled)
1630 {
1631   GtkTreeModel *model;
1632   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1633
1634   /* Update the status in the model */
1635   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1636
1637   /* Update the status-infobar in the details view when disabling */
1638   if (!enabled)
1639     accounts_dialog_update_status_infobar (dialog, account);
1640
1641   DEBUG ("Account %s is now %s",
1642       tp_account_get_display_name (account),
1643       enabled ? "enabled" : "disabled");
1644 }
1645
1646 static void
1647 accounts_dialog_account_disabled_cb (TpAccountManager *manager,
1648     TpAccount *account,
1649     EmpathyAccountsDialog *dialog)
1650 {
1651   enable_or_disable_account (dialog, account, FALSE);
1652   update_account_in_treeview (dialog, account);
1653 }
1654
1655 static void
1656 accounts_dialog_account_enabled_cb (TpAccountManager *manager,
1657     TpAccount *account,
1658     EmpathyAccountsDialog *dialog)
1659 {
1660   enable_or_disable_account (dialog, account, TRUE);
1661 }
1662
1663 static void
1664 accounts_dialog_button_import_clicked_cb (GtkWidget *button,
1665     EmpathyAccountsDialog *dialog)
1666 {
1667   GtkWidget *import_dialog;
1668
1669   import_dialog = empathy_import_dialog_new (GTK_WINDOW (dialog),
1670       FALSE);
1671   gtk_widget_show (import_dialog);
1672 }
1673
1674 static void
1675 accounts_dialog_close_response_cb (GtkDialog *message_dialog,
1676   gint response_id,
1677   gpointer user_data)
1678 {
1679   GtkWidget *account_dialog = GTK_WIDGET (user_data);
1680
1681   gtk_widget_destroy (GTK_WIDGET (message_dialog));
1682
1683   if (response_id == GTK_RESPONSE_YES)
1684     gtk_widget_destroy (account_dialog);
1685 }
1686
1687 static gboolean
1688 accounts_dialog_delete_event_cb (GtkWidget *widget,
1689     GdkEvent *event,
1690     EmpathyAccountsDialog *dialog)
1691 {
1692   /* we maunally handle responses to delete events */
1693   return TRUE;
1694 }
1695
1696 static void
1697 accounts_dialog_set_selected_account (EmpathyAccountsDialog *dialog,
1698     TpAccount *account)
1699 {
1700   GtkTreeSelection *selection;
1701   GtkTreeIter       iter;
1702   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1703
1704   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1705   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1706     gtk_tree_selection_select_iter (selection, &iter);
1707 }
1708
1709 static void
1710 accounts_dialog_cms_ready_cb (EmpathyConnectionManagers *cms,
1711     GParamSpec *pspec,
1712     EmpathyAccountsDialog *dialog)
1713 {
1714   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1715
1716   if (empathy_connection_managers_is_ready (cms))
1717     {
1718       accounts_dialog_update_settings (dialog, NULL);
1719
1720       if (priv->initial_selection != NULL)
1721         {
1722           accounts_dialog_set_selected_account
1723               (dialog, priv->initial_selection);
1724           g_object_unref (priv->initial_selection);
1725           priv->initial_selection = NULL;
1726         }
1727     }
1728 }
1729
1730 static void
1731 accounts_dialog_accounts_setup (EmpathyAccountsDialog *dialog)
1732 {
1733   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1734   GList *accounts, *l;
1735
1736   g_signal_connect (priv->account_manager, "account-validity-changed",
1737       G_CALLBACK (accounts_dialog_account_validity_changed_cb),
1738       dialog);
1739   g_signal_connect (priv->account_manager, "account-removed",
1740       G_CALLBACK (accounts_dialog_account_removed_cb),
1741       dialog);
1742   g_signal_connect (priv->account_manager, "account-enabled",
1743       G_CALLBACK (accounts_dialog_account_enabled_cb),
1744       dialog);
1745   g_signal_connect (priv->account_manager, "account-disabled",
1746       G_CALLBACK (accounts_dialog_account_disabled_cb),
1747       dialog);
1748
1749   /* Add existing accounts */
1750   accounts = tp_account_manager_get_valid_accounts (priv->account_manager);
1751   for (l = accounts; l; l = l->next)
1752     {
1753       accounts_dialog_add_account (dialog, l->data);
1754
1755       empathy_signal_connect_weak (l->data, "status-changed",
1756           G_CALLBACK (accounts_dialog_connection_changed_cb), G_OBJECT (dialog));
1757       empathy_signal_connect_weak (l->data, "presence-changed",
1758           G_CALLBACK (accounts_dialog_presence_changed_cb), G_OBJECT (dialog));
1759     }
1760   g_list_free (accounts);
1761
1762   priv->cms = empathy_connection_managers_dup_singleton ();
1763   if (!empathy_connection_managers_is_ready (priv->cms))
1764     g_signal_connect (priv->cms, "notify::ready",
1765         G_CALLBACK (accounts_dialog_cms_ready_cb), dialog);
1766
1767   accounts_dialog_model_select_first (dialog);
1768 }
1769
1770 static void
1771 accounts_dialog_manager_ready_cb (GObject *source_object,
1772     GAsyncResult *result,
1773     gpointer user_data)
1774 {
1775   TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object);
1776   GError *error = NULL;
1777
1778   if (!tp_account_manager_prepare_finish (manager, result, &error))
1779     {
1780       DEBUG ("Failed to prepare account manager: %s", error->message);
1781       g_error_free (error);
1782       return;
1783     }
1784
1785   accounts_dialog_accounts_setup (user_data);
1786 }
1787
1788 static void
1789 dialog_response_cb (GtkWidget *widget,
1790     gint response_id,
1791     gpointer user_data)
1792 {
1793   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (widget);
1794
1795   if (response_id == GTK_RESPONSE_HELP)
1796     {
1797       empathy_url_show (widget, "ghelp:empathy?accounts-window");
1798     }
1799   else if (response_id == GTK_RESPONSE_CLOSE ||
1800       response_id == GTK_RESPONSE_DELETE_EVENT)
1801     {
1802       TpAccount *account = NULL;
1803
1804       if (accounts_dialog_has_pending_change (dialog, &account))
1805         {
1806           gchar *question_dialog_primary_text = get_dialog_primary_text (
1807               account);
1808
1809           accounts_dialog_show_question_dialog (dialog,
1810               question_dialog_primary_text,
1811               _("You are about to close the window, which will discard\n"
1812                   "your changes. Are you sure you want to proceed?"),
1813               G_CALLBACK (accounts_dialog_close_response_cb),
1814               widget,
1815               GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1816               GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
1817
1818           g_free (question_dialog_primary_text);
1819         }
1820       else
1821         {
1822           gtk_widget_destroy (widget);
1823         }
1824     }
1825 }
1826
1827 static void
1828 accounts_dialog_build_ui (EmpathyAccountsDialog *dialog)
1829 {
1830   GtkWidget *top_hbox;
1831   GtkBuilder                   *gui;
1832   gchar                        *filename;
1833   EmpathyAccountsDialogPriv    *priv = GET_PRIV (dialog);
1834   GtkWidget                    *content_area;
1835   GtkWidget *action_area;
1836
1837   filename = empathy_file_lookup ("empathy-accounts-dialog.ui", "src");
1838
1839   gui = empathy_builder_get_file (filename,
1840       "accounts_dialog_hbox", &top_hbox,
1841       "vbox_details", &priv->vbox_details,
1842       "frame_no_protocol", &priv->frame_no_protocol,
1843       "alignment_settings", &priv->alignment_settings,
1844       "alignment_infobar", &priv->alignment_infobar,
1845       "treeview", &priv->treeview,
1846       "image_type", &priv->image_type,
1847       "label_name", &priv->label_name,
1848       "button_add", &priv->button_add,
1849       "button_import", &priv->button_import,
1850       "hbox_protocol", &priv->hbox_protocol,
1851       NULL);
1852   g_free (filename);
1853
1854   empathy_builder_connect (gui, dialog,
1855       "button_add", "clicked", accounts_dialog_button_add_clicked_cb,
1856       "button_import", "clicked", accounts_dialog_button_import_clicked_cb,
1857       NULL);
1858
1859   content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
1860
1861   gtk_container_add (GTK_CONTAINER (content_area), top_hbox);
1862
1863   g_object_unref (gui);
1864
1865   action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
1866
1867 #ifdef HAVE_MOBLIN
1868   gtk_widget_hide (action_area);
1869 #endif
1870
1871   priv->combobox_protocol = empathy_protocol_chooser_new ();
1872   gtk_box_pack_start (GTK_BOX (priv->hbox_protocol), priv->combobox_protocol,
1873       TRUE, TRUE, 0);
1874   g_signal_connect (priv->combobox_protocol, "changed",
1875       G_CALLBACK (accounts_dialog_protocol_changed_cb),
1876       dialog);
1877
1878   if (priv->parent_window)
1879     gtk_window_set_transient_for (GTK_WINDOW (dialog),
1880         priv->parent_window);
1881
1882   /* set up spinner */
1883   priv->throbber = ephy_spinner_new ();
1884   ephy_spinner_set_size (EPHY_SPINNER (priv->throbber), GTK_ICON_SIZE_SMALL_TOOLBAR);
1885
1886   priv->infobar = gtk_info_bar_new ();
1887   gtk_container_add (GTK_CONTAINER (priv->alignment_infobar),
1888       priv->infobar);
1889   gtk_widget_show (priv->infobar);
1890
1891   priv->image_status = gtk_image_new_from_icon_name (
1892             empathy_icon_name_for_presence (
1893             TP_CONNECTION_PRESENCE_TYPE_OFFLINE), GTK_ICON_SIZE_SMALL_TOOLBAR);
1894
1895   priv->label_status = gtk_label_new (NULL);
1896
1897   content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (priv->infobar));
1898   gtk_box_pack_start (GTK_BOX (content_area), priv->throbber,
1899       FALSE, FALSE, 0);
1900   gtk_box_pack_start (GTK_BOX (content_area), priv->image_status,
1901       FALSE, FALSE, 0);
1902   gtk_container_add (GTK_CONTAINER (content_area), priv->label_status);
1903
1904   /* Tweak the dialog */
1905   gtk_window_set_title (GTK_WINDOW (dialog), _("Accounts"));
1906   gtk_window_set_role (GTK_WINDOW (dialog), "accounts");
1907
1908   gtk_window_set_default_size (GTK_WINDOW (dialog), 640, -1);
1909
1910   gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
1911
1912   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
1913
1914   /* add dialog buttons */
1915   gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), GTK_BUTTONBOX_END);
1916
1917   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1918       GTK_STOCK_HELP, GTK_RESPONSE_HELP,
1919       GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1920       NULL);
1921
1922   g_signal_connect (dialog, "response",
1923       G_CALLBACK (dialog_response_cb), dialog);
1924
1925   g_signal_connect (dialog, "delete-event",
1926       G_CALLBACK (accounts_dialog_delete_event_cb), dialog);
1927 }
1928
1929 static void
1930 do_dispose (GObject *obj)
1931 {
1932   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (obj);
1933   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1934
1935   if (priv->dispose_has_run)
1936     return;
1937
1938   priv->dispose_has_run = TRUE;
1939
1940   /* Disconnect signals */
1941   g_signal_handlers_disconnect_by_func (priv->account_manager,
1942       accounts_dialog_account_validity_changed_cb,
1943       dialog);
1944   g_signal_handlers_disconnect_by_func (priv->account_manager,
1945       accounts_dialog_account_removed_cb,
1946       dialog);
1947   g_signal_handlers_disconnect_by_func (priv->account_manager,
1948       accounts_dialog_account_enabled_cb,
1949       dialog);
1950   g_signal_handlers_disconnect_by_func (priv->account_manager,
1951       accounts_dialog_account_disabled_cb,
1952       dialog);
1953   g_signal_handlers_disconnect_by_func (priv->account_manager,
1954       accounts_dialog_manager_ready_cb,
1955       dialog);
1956
1957   if (priv->connecting_id)
1958     g_source_remove (priv->connecting_id);
1959
1960   if (priv->account_manager != NULL)
1961     {
1962       g_object_unref (priv->account_manager);
1963       priv->account_manager = NULL;
1964     }
1965
1966   if (priv->cms != NULL)
1967     {
1968       g_object_unref (priv->cms);
1969       priv->cms = NULL;
1970     }
1971
1972   if (priv->initial_selection != NULL)
1973     g_object_unref (priv->initial_selection);
1974   priv->initial_selection = NULL;
1975
1976   G_OBJECT_CLASS (empathy_accounts_dialog_parent_class)->dispose (obj);
1977 }
1978
1979 static GObject *
1980 do_constructor (GType type,
1981     guint n_props,
1982     GObjectConstructParam *props)
1983 {
1984   GObject *retval;
1985
1986   if (dialog_singleton)
1987     {
1988       retval = G_OBJECT (dialog_singleton);
1989     }
1990   else
1991     {
1992       retval =
1993         G_OBJECT_CLASS (empathy_accounts_dialog_parent_class)->constructor
1994             (type, n_props, props);
1995
1996       dialog_singleton = EMPATHY_ACCOUNTS_DIALOG (retval);
1997       g_object_add_weak_pointer (retval, (gpointer) &dialog_singleton);
1998     }
1999
2000   return retval;
2001 }
2002
2003 static void
2004 do_get_property (GObject *object,
2005     guint property_id,
2006     GValue *value,
2007     GParamSpec *pspec)
2008 {
2009   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
2010
2011   switch (property_id)
2012     {
2013     case PROP_PARENT:
2014       g_value_set_object (value, priv->parent_window);
2015       break;
2016     default:
2017       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2018     }
2019 }
2020
2021 static void
2022 do_set_property (GObject *object,
2023     guint property_id,
2024     const GValue *value,
2025     GParamSpec *pspec)
2026 {
2027   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
2028
2029   switch (property_id)
2030     {
2031     case PROP_PARENT:
2032       priv->parent_window = g_value_get_object (value);
2033       break;
2034     default:
2035       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2036     }
2037 }
2038
2039 static void
2040 do_constructed (GObject *object)
2041 {
2042   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (object);
2043   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2044   gboolean import_asked;
2045
2046   accounts_dialog_build_ui (dialog);
2047   accounts_dialog_model_setup (dialog);
2048
2049   /* Set up signalling */
2050   priv->account_manager = tp_account_manager_dup ();
2051
2052   tp_account_manager_prepare_async (priv->account_manager, NULL,
2053       accounts_dialog_manager_ready_cb, dialog);
2054
2055   empathy_conf_get_bool (empathy_conf_get (),
2056       EMPATHY_PREFS_IMPORT_ASKED, &import_asked);
2057
2058   if (empathy_import_accounts_to_import ())
2059     {
2060       gtk_widget_show (priv->button_import);
2061
2062       if (!import_asked)
2063         {
2064           GtkWidget *import_dialog;
2065
2066           empathy_conf_set_bool (empathy_conf_get (),
2067               EMPATHY_PREFS_IMPORT_ASKED, TRUE);
2068           import_dialog = empathy_import_dialog_new (GTK_WINDOW (dialog),
2069               FALSE);
2070           gtk_widget_show (import_dialog);
2071         }
2072     }
2073 }
2074
2075 static void
2076 empathy_accounts_dialog_class_init (EmpathyAccountsDialogClass *klass)
2077 {
2078   GObjectClass *oclass = G_OBJECT_CLASS (klass);
2079   GParamSpec *param_spec;
2080
2081   oclass->constructor = do_constructor;
2082   oclass->dispose = do_dispose;
2083   oclass->constructed = do_constructed;
2084   oclass->set_property = do_set_property;
2085   oclass->get_property = do_get_property;
2086
2087   param_spec = g_param_spec_object ("parent",
2088       "parent", "The parent window",
2089       GTK_TYPE_WINDOW,
2090       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2091   g_object_class_install_property (oclass, PROP_PARENT, param_spec);
2092
2093   g_type_class_add_private (klass, sizeof (EmpathyAccountsDialogPriv));
2094 }
2095
2096 static void
2097 empathy_accounts_dialog_init (EmpathyAccountsDialog *dialog)
2098 {
2099   EmpathyAccountsDialogPriv *priv;
2100
2101   priv = G_TYPE_INSTANCE_GET_PRIVATE ((dialog),
2102       EMPATHY_TYPE_ACCOUNTS_DIALOG,
2103       EmpathyAccountsDialogPriv);
2104   dialog->priv = priv;
2105 }
2106
2107 /* public methods */
2108
2109 GtkWidget *
2110 empathy_accounts_dialog_show (GtkWindow *parent,
2111     TpAccount *selected_account)
2112 {
2113   EmpathyAccountsDialog *dialog;
2114   EmpathyAccountsDialogPriv *priv;
2115
2116   dialog = g_object_new (EMPATHY_TYPE_ACCOUNTS_DIALOG,
2117       "parent", parent, NULL);
2118
2119   priv = GET_PRIV (dialog);
2120
2121   if (selected_account)
2122     {
2123       if (priv->cms != NULL && empathy_connection_managers_is_ready (priv->cms))
2124         accounts_dialog_set_selected_account (dialog, selected_account);
2125       else
2126         /* save the selection to set it later when the cms
2127          * becomes ready.
2128          */
2129         priv->initial_selection = g_object_ref (selected_account);
2130     }
2131
2132   gtk_window_present (GTK_WINDOW (dialog));
2133
2134   return GTK_WIDGET (dialog);
2135 }