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