]> git.0d.be Git - empathy.git/blob - src/empathy-accounts-dialog.c
accounts-dialog: display status of accounts in the treeview (#605309)
[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 accounts_dialog_presence_changed_cb (TpAccount *account,
1475     guint presence,
1476     gchar *status,
1477     gchar *status_message,
1478     EmpathyAccountsDialog *dialog)
1479 {
1480   /* Update the status-infobar in the details view */
1481   accounts_dialog_update_status_infobar (dialog, account);
1482 }
1483
1484 static void
1485 accounts_dialog_account_display_name_changed_cb (TpAccount *account,
1486   GParamSpec *pspec,
1487   gpointer user_data)
1488 {
1489   const gchar *display_name;
1490   GtkTreeIter iter;
1491   GtkTreeModel *model;
1492   EmpathyAccountSettings *settings;
1493   TpAccount *selected_account;
1494   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1495   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1496
1497   display_name = tp_account_get_display_name (account);
1498   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1499   settings = accounts_dialog_model_get_selected_settings (dialog);
1500   if (settings == NULL)
1501     return;
1502
1503   selected_account = empathy_account_settings_get_account (settings);
1504
1505   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1506     {
1507       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1508           COL_NAME, display_name,
1509           -1);
1510     }
1511
1512   if (selected_account == account)
1513     accounts_dialog_update_name_label (dialog, display_name);
1514
1515   g_object_unref (settings);
1516 }
1517
1518 static void
1519 accounts_dialog_add_account (EmpathyAccountsDialog *dialog,
1520     TpAccount *account)
1521 {
1522   EmpathyAccountSettings *settings;
1523   GtkTreeModel       *model;
1524   GtkTreeIter         iter;
1525   TpConnectionStatus  status;
1526   const gchar        *name;
1527   gboolean            enabled;
1528   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1529
1530   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1531   status = tp_account_get_connection_status (account, NULL);
1532   name = tp_account_get_display_name (account);
1533   enabled = tp_account_is_enabled (account);
1534
1535   settings = empathy_account_settings_new_for_account (account);
1536
1537   if (!accounts_dialog_get_account_iter (dialog, account, &iter))
1538     gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1539
1540   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1541       COL_NAME, name,
1542       COL_STATUS, status,
1543       COL_ACCOUNT_POINTER, account,
1544       COL_ACCOUNT_SETTINGS_POINTER, settings,
1545       -1);
1546
1547   accounts_dialog_connection_changed_cb (account,
1548       0,
1549       status,
1550       TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED,
1551       NULL,
1552       NULL,
1553       dialog);
1554
1555   empathy_signal_connect_weak (account, "notify::display-name",
1556       G_CALLBACK (accounts_dialog_account_display_name_changed_cb),
1557       G_OBJECT (dialog));
1558
1559   g_object_unref (settings);
1560 }
1561
1562 static void
1563 account_prepare_cb (GObject *source_object,
1564     GAsyncResult *result,
1565     gpointer user_data)
1566 {
1567   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1568   TpAccount *account = TP_ACCOUNT (source_object);
1569   GError *error = NULL;
1570
1571   if (!tp_account_prepare_finish (account, result, &error))
1572     {
1573       DEBUG ("Failed to prepare account: %s", error->message);
1574       g_error_free (error);
1575       return;
1576     }
1577
1578   accounts_dialog_add_account (dialog, account);
1579 }
1580
1581 static void
1582 accounts_dialog_account_validity_changed_cb (TpAccountManager *manager,
1583     TpAccount *account,
1584     gboolean valid,
1585     EmpathyAccountsDialog *dialog)
1586 {
1587   tp_account_prepare_async (account, NULL, account_prepare_cb, dialog);
1588 }
1589
1590 static void
1591 accounts_dialog_account_removed_cb (TpAccountManager *manager,
1592     TpAccount *account,
1593     EmpathyAccountsDialog *dialog)
1594 {
1595   GtkTreeIter iter;
1596   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1597
1598   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1599     {
1600       gtk_list_store_remove (GTK_LIST_STORE (
1601             gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview))), &iter);
1602     }
1603 }
1604
1605 static void
1606 enable_or_disable_account (EmpathyAccountsDialog *dialog,
1607     TpAccount *account,
1608     gboolean enabled)
1609 {
1610   GtkTreeModel *model;
1611   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1612
1613   /* Update the status in the model */
1614   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1615
1616   /* Update the status-infobar in the details view when disabling */
1617   if (!enabled)
1618     accounts_dialog_update_status_infobar (dialog, account);
1619
1620   DEBUG ("Account %s is now %s",
1621       tp_account_get_display_name (account),
1622       enabled ? "enabled" : "disabled");
1623 }
1624
1625 static void
1626 accounts_dialog_account_disabled_cb (TpAccountManager *manager,
1627     TpAccount *account,
1628     EmpathyAccountsDialog *dialog)
1629 {
1630   enable_or_disable_account (dialog, account, FALSE);
1631 }
1632
1633 static void
1634 accounts_dialog_account_enabled_cb (TpAccountManager *manager,
1635     TpAccount *account,
1636     EmpathyAccountsDialog *dialog)
1637 {
1638   enable_or_disable_account (dialog, account, TRUE);
1639 }
1640
1641 static void
1642 accounts_dialog_button_import_clicked_cb (GtkWidget *button,
1643     EmpathyAccountsDialog *dialog)
1644 {
1645   GtkWidget *import_dialog;
1646
1647   import_dialog = empathy_import_dialog_new (GTK_WINDOW (dialog),
1648       FALSE);
1649   gtk_widget_show (import_dialog);
1650 }
1651
1652 static void
1653 accounts_dialog_close_response_cb (GtkDialog *message_dialog,
1654   gint response_id,
1655   gpointer user_data)
1656 {
1657   GtkWidget *account_dialog = GTK_WIDGET (user_data);
1658
1659   gtk_widget_destroy (GTK_WIDGET (message_dialog));
1660
1661   if (response_id == GTK_RESPONSE_YES)
1662     gtk_widget_destroy (account_dialog);
1663 }
1664
1665 static gboolean
1666 accounts_dialog_delete_event_cb (GtkWidget *widget,
1667     GdkEvent *event,
1668     EmpathyAccountsDialog *dialog)
1669 {
1670   /* we maunally handle responses to delete events */
1671   return TRUE;
1672 }
1673
1674 static void
1675 accounts_dialog_set_selected_account (EmpathyAccountsDialog *dialog,
1676     TpAccount *account)
1677 {
1678   GtkTreeSelection *selection;
1679   GtkTreeIter       iter;
1680   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1681
1682   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1683   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1684     gtk_tree_selection_select_iter (selection, &iter);
1685 }
1686
1687 static void
1688 accounts_dialog_cms_ready_cb (EmpathyConnectionManagers *cms,
1689     GParamSpec *pspec,
1690     EmpathyAccountsDialog *dialog)
1691 {
1692   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1693
1694   if (empathy_connection_managers_is_ready (cms))
1695     {
1696       accounts_dialog_update_settings (dialog, NULL);
1697
1698       if (priv->initial_selection != NULL)
1699         {
1700           accounts_dialog_set_selected_account
1701               (dialog, priv->initial_selection);
1702           g_object_unref (priv->initial_selection);
1703           priv->initial_selection = NULL;
1704         }
1705     }
1706 }
1707
1708 static void
1709 accounts_dialog_accounts_setup (EmpathyAccountsDialog *dialog)
1710 {
1711   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1712   GList *accounts, *l;
1713
1714   g_signal_connect (priv->account_manager, "account-validity-changed",
1715       G_CALLBACK (accounts_dialog_account_validity_changed_cb),
1716       dialog);
1717   g_signal_connect (priv->account_manager, "account-removed",
1718       G_CALLBACK (accounts_dialog_account_removed_cb),
1719       dialog);
1720   g_signal_connect (priv->account_manager, "account-enabled",
1721       G_CALLBACK (accounts_dialog_account_enabled_cb),
1722       dialog);
1723   g_signal_connect (priv->account_manager, "account-disabled",
1724       G_CALLBACK (accounts_dialog_account_disabled_cb),
1725       dialog);
1726
1727   /* Add existing accounts */
1728   accounts = tp_account_manager_get_valid_accounts (priv->account_manager);
1729   for (l = accounts; l; l = l->next)
1730     {
1731       accounts_dialog_add_account (dialog, l->data);
1732
1733       empathy_signal_connect_weak (l->data, "status-changed",
1734           G_CALLBACK (accounts_dialog_connection_changed_cb), G_OBJECT (dialog));
1735       empathy_signal_connect_weak (l->data, "presence-changed",
1736           G_CALLBACK (accounts_dialog_presence_changed_cb), G_OBJECT (dialog));
1737     }
1738   g_list_free (accounts);
1739
1740   priv->cms = empathy_connection_managers_dup_singleton ();
1741   if (!empathy_connection_managers_is_ready (priv->cms))
1742     g_signal_connect (priv->cms, "notify::ready",
1743         G_CALLBACK (accounts_dialog_cms_ready_cb), dialog);
1744
1745   accounts_dialog_model_select_first (dialog);
1746 }
1747
1748 static void
1749 accounts_dialog_manager_ready_cb (GObject *source_object,
1750     GAsyncResult *result,
1751     gpointer user_data)
1752 {
1753   TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object);
1754   GError *error = NULL;
1755
1756   if (!tp_account_manager_prepare_finish (manager, result, &error))
1757     {
1758       DEBUG ("Failed to prepare account manager: %s", error->message);
1759       g_error_free (error);
1760       return;
1761     }
1762
1763   accounts_dialog_accounts_setup (user_data);
1764 }
1765
1766 static void
1767 dialog_response_cb (GtkWidget *widget,
1768     gint response_id,
1769     gpointer user_data)
1770 {
1771   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (widget);
1772
1773   if (response_id == GTK_RESPONSE_HELP)
1774     {
1775       empathy_url_show (widget, "ghelp:empathy?accounts-window");
1776     }
1777   else if (response_id == GTK_RESPONSE_CLOSE ||
1778       response_id == GTK_RESPONSE_DELETE_EVENT)
1779     {
1780       TpAccount *account = NULL;
1781
1782       if (accounts_dialog_has_pending_change (dialog, &account))
1783         {
1784           gchar *question_dialog_primary_text = get_dialog_primary_text (
1785               account);
1786
1787           accounts_dialog_show_question_dialog (dialog,
1788               question_dialog_primary_text,
1789               _("You are about to close the window, which will discard\n"
1790                   "your changes. Are you sure you want to proceed?"),
1791               G_CALLBACK (accounts_dialog_close_response_cb),
1792               widget,
1793               GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1794               GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
1795
1796           g_free (question_dialog_primary_text);
1797         }
1798       else
1799         {
1800           gtk_widget_destroy (widget);
1801         }
1802     }
1803 }
1804
1805 static void
1806 accounts_dialog_build_ui (EmpathyAccountsDialog *dialog)
1807 {
1808   GtkWidget *top_hbox;
1809   GtkBuilder                   *gui;
1810   gchar                        *filename;
1811   EmpathyAccountsDialogPriv    *priv = GET_PRIV (dialog);
1812   GtkWidget                    *content_area;
1813   GtkWidget *action_area;
1814
1815   filename = empathy_file_lookup ("empathy-accounts-dialog.ui", "src");
1816
1817   gui = empathy_builder_get_file (filename,
1818       "accounts_dialog_hbox", &top_hbox,
1819       "vbox_details", &priv->vbox_details,
1820       "frame_no_protocol", &priv->frame_no_protocol,
1821       "alignment_settings", &priv->alignment_settings,
1822       "alignment_infobar", &priv->alignment_infobar,
1823       "treeview", &priv->treeview,
1824       "image_type", &priv->image_type,
1825       "label_name", &priv->label_name,
1826       "button_add", &priv->button_add,
1827       "button_import", &priv->button_import,
1828       "hbox_protocol", &priv->hbox_protocol,
1829       NULL);
1830   g_free (filename);
1831
1832   empathy_builder_connect (gui, dialog,
1833       "button_add", "clicked", accounts_dialog_button_add_clicked_cb,
1834       "button_import", "clicked", accounts_dialog_button_import_clicked_cb,
1835       NULL);
1836
1837   content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
1838
1839   gtk_container_add (GTK_CONTAINER (content_area), top_hbox);
1840
1841   g_object_unref (gui);
1842
1843   action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
1844
1845 #ifdef HAVE_MOBLIN
1846   gtk_widget_hide (action_area);
1847 #endif
1848
1849   priv->combobox_protocol = empathy_protocol_chooser_new ();
1850   gtk_box_pack_start (GTK_BOX (priv->hbox_protocol), priv->combobox_protocol,
1851       TRUE, TRUE, 0);
1852   g_signal_connect (priv->combobox_protocol, "changed",
1853       G_CALLBACK (accounts_dialog_protocol_changed_cb),
1854       dialog);
1855
1856   if (priv->parent_window)
1857     gtk_window_set_transient_for (GTK_WINDOW (dialog),
1858         priv->parent_window);
1859
1860   /* set up spinner */
1861   priv->throbber = ephy_spinner_new ();
1862   ephy_spinner_set_size (EPHY_SPINNER (priv->throbber), GTK_ICON_SIZE_SMALL_TOOLBAR);
1863
1864   priv->infobar = gtk_info_bar_new ();
1865   gtk_container_add (GTK_CONTAINER (priv->alignment_infobar),
1866       priv->infobar);
1867   gtk_widget_show (priv->infobar);
1868
1869   priv->image_status = gtk_image_new_from_icon_name (
1870             empathy_icon_name_for_presence (
1871             TP_CONNECTION_PRESENCE_TYPE_OFFLINE), GTK_ICON_SIZE_SMALL_TOOLBAR);
1872
1873   priv->label_status = gtk_label_new (NULL);
1874
1875   content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (priv->infobar));
1876   gtk_box_pack_start (GTK_BOX (content_area), priv->throbber,
1877       FALSE, FALSE, 0);
1878   gtk_box_pack_start (GTK_BOX (content_area), priv->image_status,
1879       FALSE, FALSE, 0);
1880   gtk_container_add (GTK_CONTAINER (content_area), priv->label_status);
1881
1882   /* Tweak the dialog */
1883   gtk_window_set_title (GTK_WINDOW (dialog), _("Accounts"));
1884   gtk_window_set_role (GTK_WINDOW (dialog), "accounts");
1885
1886   gtk_window_set_default_size (GTK_WINDOW (dialog), 640, -1);
1887
1888   gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
1889
1890   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
1891
1892   /* add dialog buttons */
1893   gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), GTK_BUTTONBOX_END);
1894
1895   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1896       GTK_STOCK_HELP, GTK_RESPONSE_HELP,
1897       GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1898       NULL);
1899
1900   g_signal_connect (dialog, "response",
1901       G_CALLBACK (dialog_response_cb), dialog);
1902
1903   g_signal_connect (dialog, "delete-event",
1904       G_CALLBACK (accounts_dialog_delete_event_cb), dialog);
1905 }
1906
1907 static void
1908 do_dispose (GObject *obj)
1909 {
1910   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (obj);
1911   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1912
1913   if (priv->dispose_has_run)
1914     return;
1915
1916   priv->dispose_has_run = TRUE;
1917
1918   /* Disconnect signals */
1919   g_signal_handlers_disconnect_by_func (priv->account_manager,
1920       accounts_dialog_account_validity_changed_cb,
1921       dialog);
1922   g_signal_handlers_disconnect_by_func (priv->account_manager,
1923       accounts_dialog_account_removed_cb,
1924       dialog);
1925   g_signal_handlers_disconnect_by_func (priv->account_manager,
1926       accounts_dialog_account_enabled_cb,
1927       dialog);
1928   g_signal_handlers_disconnect_by_func (priv->account_manager,
1929       accounts_dialog_account_disabled_cb,
1930       dialog);
1931   g_signal_handlers_disconnect_by_func (priv->account_manager,
1932       accounts_dialog_manager_ready_cb,
1933       dialog);
1934
1935   if (priv->connecting_id)
1936     g_source_remove (priv->connecting_id);
1937
1938   if (priv->account_manager != NULL)
1939     {
1940       g_object_unref (priv->account_manager);
1941       priv->account_manager = NULL;
1942     }
1943
1944   if (priv->cms != NULL)
1945     {
1946       g_object_unref (priv->cms);
1947       priv->cms = NULL;
1948     }
1949
1950   if (priv->initial_selection != NULL)
1951     g_object_unref (priv->initial_selection);
1952   priv->initial_selection = NULL;
1953
1954   G_OBJECT_CLASS (empathy_accounts_dialog_parent_class)->dispose (obj);
1955 }
1956
1957 static GObject *
1958 do_constructor (GType type,
1959     guint n_props,
1960     GObjectConstructParam *props)
1961 {
1962   GObject *retval;
1963
1964   if (dialog_singleton)
1965     {
1966       retval = G_OBJECT (dialog_singleton);
1967     }
1968   else
1969     {
1970       retval =
1971         G_OBJECT_CLASS (empathy_accounts_dialog_parent_class)->constructor
1972             (type, n_props, props);
1973
1974       dialog_singleton = EMPATHY_ACCOUNTS_DIALOG (retval);
1975       g_object_add_weak_pointer (retval, (gpointer) &dialog_singleton);
1976     }
1977
1978   return retval;
1979 }
1980
1981 static void
1982 do_get_property (GObject *object,
1983     guint property_id,
1984     GValue *value,
1985     GParamSpec *pspec)
1986 {
1987   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
1988
1989   switch (property_id)
1990     {
1991     case PROP_PARENT:
1992       g_value_set_object (value, priv->parent_window);
1993       break;
1994     default:
1995       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1996     }
1997 }
1998
1999 static void
2000 do_set_property (GObject *object,
2001     guint property_id,
2002     const GValue *value,
2003     GParamSpec *pspec)
2004 {
2005   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
2006
2007   switch (property_id)
2008     {
2009     case PROP_PARENT:
2010       priv->parent_window = g_value_get_object (value);
2011       break;
2012     default:
2013       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2014     }
2015 }
2016
2017 static void
2018 do_constructed (GObject *object)
2019 {
2020   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (object);
2021   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2022   gboolean import_asked;
2023
2024   accounts_dialog_build_ui (dialog);
2025   accounts_dialog_model_setup (dialog);
2026
2027   /* Set up signalling */
2028   priv->account_manager = tp_account_manager_dup ();
2029
2030   tp_account_manager_prepare_async (priv->account_manager, NULL,
2031       accounts_dialog_manager_ready_cb, dialog);
2032
2033   empathy_conf_get_bool (empathy_conf_get (),
2034       EMPATHY_PREFS_IMPORT_ASKED, &import_asked);
2035
2036   if (empathy_import_accounts_to_import ())
2037     {
2038       gtk_widget_show (priv->button_import);
2039
2040       if (!import_asked)
2041         {
2042           GtkWidget *import_dialog;
2043
2044           empathy_conf_set_bool (empathy_conf_get (),
2045               EMPATHY_PREFS_IMPORT_ASKED, TRUE);
2046           import_dialog = empathy_import_dialog_new (GTK_WINDOW (dialog),
2047               FALSE);
2048           gtk_widget_show (import_dialog);
2049         }
2050     }
2051 }
2052
2053 static void
2054 empathy_accounts_dialog_class_init (EmpathyAccountsDialogClass *klass)
2055 {
2056   GObjectClass *oclass = G_OBJECT_CLASS (klass);
2057   GParamSpec *param_spec;
2058
2059   oclass->constructor = do_constructor;
2060   oclass->dispose = do_dispose;
2061   oclass->constructed = do_constructed;
2062   oclass->set_property = do_set_property;
2063   oclass->get_property = do_get_property;
2064
2065   param_spec = g_param_spec_object ("parent",
2066       "parent", "The parent window",
2067       GTK_TYPE_WINDOW,
2068       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2069   g_object_class_install_property (oclass, PROP_PARENT, param_spec);
2070
2071   g_type_class_add_private (klass, sizeof (EmpathyAccountsDialogPriv));
2072 }
2073
2074 static void
2075 empathy_accounts_dialog_init (EmpathyAccountsDialog *dialog)
2076 {
2077   EmpathyAccountsDialogPriv *priv;
2078
2079   priv = G_TYPE_INSTANCE_GET_PRIVATE ((dialog),
2080       EMPATHY_TYPE_ACCOUNTS_DIALOG,
2081       EmpathyAccountsDialogPriv);
2082   dialog->priv = priv;
2083 }
2084
2085 /* public methods */
2086
2087 GtkWidget *
2088 empathy_accounts_dialog_show (GtkWindow *parent,
2089     TpAccount *selected_account)
2090 {
2091   EmpathyAccountsDialog *dialog;
2092   EmpathyAccountsDialogPriv *priv;
2093
2094   dialog = g_object_new (EMPATHY_TYPE_ACCOUNTS_DIALOG,
2095       "parent", parent, NULL);
2096
2097   priv = GET_PRIV (dialog);
2098
2099   if (selected_account)
2100     {
2101       if (priv->cms != NULL && empathy_connection_managers_is_ready (priv->cms))
2102         accounts_dialog_set_selected_account (dialog, selected_account);
2103       else
2104         /* save the selection to set it later when the cms
2105          * becomes ready.
2106          */
2107         priv->initial_selection = g_object_ref (selected_account);
2108     }
2109
2110   gtk_window_present (GTK_WINDOW (dialog));
2111
2112   return GTK_WIDGET (dialog);
2113 }