]> git.0d.be Git - empathy.git/blob - src/empathy-accounts-dialog.c
accounts-dialog: keep a pointer on the text renderer
[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  *          Danielle Madeley <danielle.madeley@collabora.co.uk>
25  */
26
27 #include <config.h>
28
29 #include <string.h>
30 #include <stdlib.h>
31
32 #include <gtk/gtk.h>
33 #include <glib/gi18n-lib.h>
34 #include <dbus/dbus-glib.h>
35 #include <gio/gdesktopappinfo.h>
36
37 #include <telepathy-glib/account-manager.h>
38 #include <telepathy-glib/defs.h>
39 #include <telepathy-glib/util.h>
40
41 #include <libempathy/empathy-utils.h>
42 #include <libempathy/empathy-connection-managers.h>
43 #include <libempathy/empathy-connectivity.h>
44 #include <libempathy/empathy-pkg-kit.h>
45 #include <libempathy/empathy-tp-contact-factory.h>
46
47 #include <libempathy-gtk/empathy-ui-utils.h>
48 #include <libempathy-gtk/empathy-protocol-chooser.h>
49 #include <libempathy-gtk/empathy-account-widget.h>
50 #include <libempathy-gtk/empathy-account-widget-irc.h>
51 #include <libempathy-gtk/empathy-account-widget-sip.h>
52 #include <libempathy-gtk/empathy-cell-renderer-activatable.h>
53 #include <libempathy-gtk/empathy-contact-widget.h>
54 #include <libempathy-gtk/empathy-images.h>
55 #include <libempathy-gtk/empathy-local-xmpp-assistant-widget.h>
56 #include <libempathy-gtk/empathy-new-account-dialog.h>
57
58 #include "empathy-accounts-common.h"
59 #include "empathy-accounts-dialog.h"
60 #include "empathy-import-dialog.h"
61 #include "empathy-import-utils.h"
62
63 #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
64 #include <libempathy/empathy-debug.h>
65
66 /* Flashing delay for icons (milliseconds). */
67 #define FLASH_TIMEOUT 500
68
69 /* The primary text of the dialog shown to the user when he is about to lose
70  * unsaved changes */
71 #define PENDING_CHANGES_QUESTION_PRIMARY_TEXT \
72   _("There are unsaved modifications to your %s account.")
73 /* The primary text of the dialog shown to the user when he is about to lose
74  * an unsaved new account */
75 #define UNSAVED_NEW_ACCOUNT_QUESTION_PRIMARY_TEXT \
76   _("Your new account has not been saved yet.")
77
78 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccountsDialog)
79 G_DEFINE_TYPE (EmpathyAccountsDialog, empathy_accounts_dialog, GTK_TYPE_DIALOG);
80
81 enum
82 {
83   NOTEBOOK_PAGE_ACCOUNT = 0,
84   NOTEBOOK_PAGE_LOADING,
85   NOTEBOOK_PAGE_NO_PROTOCOL
86 };
87
88 typedef struct {
89   GtkWidget *alignment_settings;
90   GtkWidget *alignment_infobar;
91
92   GtkWidget *vbox_details;
93   GtkWidget *infobar;
94   GtkWidget *label_status;
95   GtkWidget *image_status;
96   GtkWidget *throbber;
97   GtkWidget *enabled_switch;
98
99   GtkWidget *treeview;
100   GtkCellRenderer *name_renderer;
101
102   GtkWidget *button_add;
103   GtkWidget *button_remove;
104   GtkWidget *button_import;
105
106   GtkWidget *image_type;
107   GtkWidget *label_name;
108   GtkWidget *label_type;
109   GtkWidget *dialog_content;
110
111   GtkWidget *notebook_account;
112   GtkWidget *spinner;
113   gboolean loading;
114
115   /* We have to keep a weak reference on the actual EmpathyAccountWidget, not
116    * just its GtkWidget. It is the only reliable source we can query to know if
117    * there are any unsaved changes to the currently selected account. We can't
118    * look at the account settings because it does not contain everything that
119    * can be changed using the EmpathyAccountWidget. For instance, it does not
120    * contain the state of the "Enabled" checkbox.
121    *
122    * Even if we create it ourself, we just get a weak ref and not a strong one
123    * as EmpathyAccountWidget unrefs itself when the GtkWidget is destroyed.
124    * That's kinda ugly; cf bgo #640417.
125    *
126    * */
127   EmpathyAccountWidget *setting_widget_object;
128
129   gboolean  connecting_show;
130   guint connecting_id;
131
132   gulong  settings_ready_id;
133   EmpathyAccountSettings *settings_ready;
134
135   TpAccountManager *account_manager;
136   EmpathyConnectionManagers *cms;
137   EmpathyConnectivity *connectivity;
138
139   GtkWindow *parent_window;
140   TpAccount *initial_selection;
141
142   /* Those are needed when changing the selected row. When a user selects
143    * another account and there are unsaved changes on the currently selected
144    * one, a confirmation message box is presented to him. Since his answer
145    * is retrieved asynchronously, we keep some information as member of the
146    * EmpathyAccountsDialog object. */
147   gboolean force_change_row;
148   GtkTreeRowReference *destination_row;
149 } EmpathyAccountsDialogPriv;
150
151 enum {
152   COL_NAME,
153   COL_STATUS,
154   COL_ACCOUNT,
155   COL_ACCOUNT_SETTINGS,
156   COL_COUNT
157 };
158
159 enum {
160   PROP_PARENT = 1
161 };
162
163 static EmpathyAccountSettings * accounts_dialog_model_get_selected_settings (
164     EmpathyAccountsDialog *dialog);
165
166 static void accounts_dialog_model_select_first (EmpathyAccountsDialog *dialog);
167
168 static void accounts_dialog_update_settings (EmpathyAccountsDialog *dialog,
169     EmpathyAccountSettings *settings);
170
171 static void accounts_dialog_model_set_selected (EmpathyAccountsDialog *dialog,
172     TpAccount *account);
173
174 static void accounts_dialog_connection_changed_cb (TpAccount *account,
175     guint old_status,
176     guint current,
177     guint reason,
178     gchar *dbus_error_name,
179     GHashTable *details,
180     EmpathyAccountsDialog *dialog);
181
182 static void accounts_dialog_presence_changed_cb (TpAccount *account,
183     guint presence,
184     gchar *status,
185     gchar *status_message,
186     EmpathyAccountsDialog *dialog);
187
188 static void accounts_dialog_model_selection_changed (
189     GtkTreeSelection *selection,
190     EmpathyAccountsDialog *dialog);
191
192 static gboolean accounts_dialog_has_pending_change (
193     EmpathyAccountsDialog *dialog, TpAccount **account);
194
195 static void
196 accounts_dialog_update_name_label (EmpathyAccountsDialog *dialog,
197     const gchar *display_name)
198 {
199   gchar *text;
200   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
201
202   text = g_markup_printf_escaped ("<b>%s</b>", display_name);
203   gtk_label_set_markup (GTK_LABEL (priv->label_name), text);
204
205   g_free (text);
206 }
207
208 static void
209 accounts_dialog_status_infobar_set_message (EmpathyAccountsDialog *dialog,
210     const gchar *message)
211 {
212   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
213   gchar *message_markup;
214
215   message_markup = g_markup_printf_escaped ("<i>%s</i>", message);
216   gtk_label_set_markup (GTK_LABEL (priv->label_status), message_markup);
217
218   gtk_widget_set_tooltip_text (priv->label_status, message);
219
220   g_free (message_markup);
221 }
222
223 static void
224 accounts_dialog_enable_account_cb (GObject *object,
225     GAsyncResult *result,
226     gpointer user_data)
227 {
228   TpAccount *account = TP_ACCOUNT (object);
229   gboolean enable = GPOINTER_TO_UINT (user_data);
230   GError *error = NULL;
231   TpAccountManager *am;
232
233   if (!tp_account_set_enabled_finish (account, result, &error))
234     {
235       DEBUG ("Could not enable the account: %s", error->message);
236       g_error_free (error);
237       return;
238     }
239
240   /* tp_account_is_enabled() is not updated yet at this point */
241   if (enable)
242     {
243       am = tp_account_manager_dup ();
244
245       empathy_connect_new_account (account, am);
246       g_object_unref (am);
247     }
248 }
249
250 static void
251 enable_and_connect_account (TpAccount *account,
252     gboolean enable)
253 {
254   tp_account_set_enabled_async (account, enable,
255       accounts_dialog_enable_account_cb, GUINT_TO_POINTER (enable));
256 }
257
258 static void
259 accounts_dialog_enable_switch_active_cb (GtkSwitch *sw,
260     GParamSpec *spec,
261     EmpathyAccountsDialog *dialog)
262 {
263   EmpathyAccountSettings *settings;
264   TpAccount *account;
265   gboolean enable;
266
267   settings = accounts_dialog_model_get_selected_settings (dialog);
268   if (settings == NULL)
269     return;
270
271   account = empathy_account_settings_get_account (settings);
272   if (account == NULL)
273     return;
274
275   enable = gtk_switch_get_active (sw);
276
277   enable_and_connect_account (account, enable);
278 }
279
280 static void
281 install_haze_cb (GObject *source,
282     GAsyncResult *result,
283     gpointer user_data)
284 {
285   GError *error = NULL;
286
287   if (!empathy_pkg_kit_install_packages_finish  (result, &error))
288     {
289       DEBUG ("Failed to install telepathy-haze: %s", error->message);
290
291       g_error_free (error);
292     }
293 }
294
295 static gboolean
296 account_is_selected (EmpathyAccountsDialog *dialog,
297     TpAccount *account)
298 {
299   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
300   GtkTreeSelection *selection;
301   GtkTreeModel *model;
302   GtkTreeIter iter;
303   TpAccount *selected_account;
304
305   if (account == NULL)
306     return FALSE;
307
308   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
309
310   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
311     return FALSE;
312
313   gtk_tree_model_get (model, &iter, COL_ACCOUNT, &selected_account, -1);
314
315   if (selected_account != NULL)
316     g_object_unref (selected_account);
317
318   return account == selected_account;
319 }
320
321 static gboolean
322 account_can_be_enabled (TpAccount *account)
323 {
324   TpStorageRestrictionFlags storage_restrictions;
325
326   storage_restrictions = tp_account_get_storage_restrictions (account);
327   if (storage_restrictions & TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_ENABLED)
328     return FALSE;
329
330   /* Butterfly accounts shouldn't be used any more */
331   if (!tp_strdiff (tp_account_get_connection_manager (account),
332         "butterfly"))
333     return FALSE;
334
335   return TRUE;
336 }
337
338 static void
339 accounts_dialog_update_status_infobar (EmpathyAccountsDialog *dialog,
340     TpAccount *account)
341 {
342   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
343   gchar                     *status_message = NULL;
344   guint                     status;
345   guint                     reason;
346   guint                     presence;
347   gboolean                  account_enabled;
348   gboolean                  creating_account;
349   gboolean display_switch = TRUE;
350
351   /* do not update the infobar when the account is not selected */
352   if (!account_is_selected (dialog, account))
353     return;
354
355   if (account != NULL)
356     {
357       status = tp_account_get_connection_status (account, &reason);
358       presence = tp_account_get_current_presence (account, NULL, &status_message);
359       account_enabled = tp_account_is_enabled (account);
360       creating_account = FALSE;
361
362       if (status == TP_CONNECTION_STATUS_CONNECTED &&
363           (presence == TP_CONNECTION_PRESENCE_TYPE_OFFLINE ||
364            presence == TP_CONNECTION_PRESENCE_TYPE_UNSET))
365         /* If presence is Unset (CM doesn't implement SimplePresence) but we
366          * are connected, consider ourself as Available.
367          * We also check Offline because of this MC5 bug: fd.o #26060 */
368         presence = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
369
370       /* set presence to offline if account is disabled
371        * (else no icon is shown in infobar)*/
372       if (!account_enabled)
373         presence = TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
374
375       display_switch = account_can_be_enabled (account);
376     }
377   else
378     {
379       status = TP_CONNECTION_STATUS_DISCONNECTED;
380       presence = TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
381       account_enabled = FALSE;
382       creating_account = TRUE;
383     }
384
385   gtk_image_set_from_icon_name (GTK_IMAGE (priv->image_status),
386       empathy_icon_name_for_presence (presence), GTK_ICON_SIZE_SMALL_TOOLBAR);
387
388   /* update the enabled switch */
389   g_signal_handlers_block_by_func (priv->enabled_switch,
390       accounts_dialog_enable_switch_active_cb, dialog);
391   gtk_switch_set_active (GTK_SWITCH (priv->enabled_switch),
392       account_enabled);
393   g_signal_handlers_unblock_by_func (priv->enabled_switch,
394       accounts_dialog_enable_switch_active_cb, dialog);
395
396   /* Display the Enable switch if account supports it */
397   gtk_widget_set_visible (priv->enabled_switch, display_switch);
398
399   if (account_enabled)
400     {
401       switch (status)
402         {
403           case TP_CONNECTION_STATUS_CONNECTING:
404             accounts_dialog_status_infobar_set_message (dialog,
405                 _("Connecting…"));
406             gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
407                 GTK_MESSAGE_INFO);
408
409             gtk_spinner_start (GTK_SPINNER (priv->throbber));
410             gtk_widget_show (priv->throbber);
411             gtk_widget_hide (priv->image_status);
412             break;
413           case TP_CONNECTION_STATUS_CONNECTED:
414             if (g_strcmp0 (status_message, "") == 0)
415               {
416                 gchar *message;
417
418                 message = g_strdup_printf ("%s",
419                     empathy_presence_get_default_message (presence));
420
421                 accounts_dialog_status_infobar_set_message (dialog, message);
422                 g_free (message);
423               }
424             else
425               {
426                 gchar *message;
427
428                 message = g_strdup_printf ("%s â€” %s",
429                     empathy_presence_get_default_message (presence),
430                     status_message);
431
432                 accounts_dialog_status_infobar_set_message (dialog, message);
433                 g_free (message);
434               }
435             gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
436                 GTK_MESSAGE_INFO);
437
438             gtk_widget_show (priv->image_status);
439             gtk_widget_hide (priv->throbber);
440             break;
441           case TP_CONNECTION_STATUS_DISCONNECTED:
442             if (reason == TP_CONNECTION_STATUS_REASON_REQUESTED)
443               {
444                 gchar *message;
445
446                 message = g_strdup_printf (_("Offline â€” %s"),
447                     empathy_account_get_error_message (account, NULL));
448                 gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
449                     GTK_MESSAGE_WARNING);
450
451                 accounts_dialog_status_infobar_set_message (dialog, message);
452                 g_free (message);
453               }
454             else
455               {
456                 gchar *message;
457
458                 message = g_strdup_printf (_("Disconnected â€” %s"),
459                     empathy_account_get_error_message (account, NULL));
460                 gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
461                     GTK_MESSAGE_ERROR);
462
463                 accounts_dialog_status_infobar_set_message (dialog, message);
464                 g_free (message);
465               }
466
467             if (!empathy_connectivity_is_online (priv->connectivity))
468                accounts_dialog_status_infobar_set_message (dialog,
469                     _("Offline â€” No Network Connection"));
470
471             gtk_spinner_stop (GTK_SPINNER (priv->throbber));
472             gtk_widget_show (priv->image_status);
473             gtk_widget_hide (priv->throbber);
474             break;
475           default:
476             accounts_dialog_status_infobar_set_message (dialog, _("Unknown Status"));
477             gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
478                 GTK_MESSAGE_WARNING);
479
480             gtk_spinner_stop (GTK_SPINNER (priv->throbber));
481             gtk_widget_hide (priv->image_status);
482             gtk_widget_hide (priv->throbber);
483         }
484     }
485   else
486     {
487       if (!tp_strdiff (tp_account_get_connection_manager (account),
488             "butterfly"))
489         {
490           const gchar *packages[] = { "telepathy-haze", NULL };
491
492           accounts_dialog_status_infobar_set_message (dialog,
493               _("This account has been disabled because it relies on an old, "
494                 "unsupported backend. Please install telepathy-haze and "
495                 "restart your session to migrate the account."));
496
497           empathy_pkg_kit_install_packages_async (0, packages, NULL, NULL,
498               install_haze_cb, NULL);
499         }
500       else
501         {
502           accounts_dialog_status_infobar_set_message (dialog,
503               _("Offline â€” Account Disabled"));
504         }
505
506       gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
507           GTK_MESSAGE_WARNING);
508       gtk_spinner_stop (GTK_SPINNER (priv->throbber));
509       gtk_widget_show (priv->image_status);
510       gtk_widget_hide (priv->throbber);
511     }
512
513   gtk_widget_show (priv->label_status);
514
515   if (!creating_account)
516     gtk_widget_show (priv->infobar);
517   else
518     gtk_widget_hide (priv->infobar);
519
520   g_free (status_message);
521 }
522
523 void
524 empathy_account_dialog_cancel (EmpathyAccountsDialog *dialog)
525 {
526   GtkTreeView *view;
527   GtkTreeModel *model;
528   GtkTreeSelection *selection;
529   GtkTreeIter iter;
530   EmpathyAccountSettings *settings;
531   TpAccount *account;
532   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
533
534   view = GTK_TREE_VIEW (priv->treeview);
535   selection = gtk_tree_view_get_selection (view);
536
537   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
538     return;
539
540   gtk_tree_model_get (model, &iter,
541       COL_ACCOUNT_SETTINGS, &settings,
542       COL_ACCOUNT, &account, -1);
543
544   empathy_account_widget_discard_pending_changes (priv->setting_widget_object);
545
546   if (account == NULL)
547     {
548       /* We were creating an account. We remove the selected row */
549       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
550     }
551   else
552     {
553       /* We were modifying an account. We discard the changes by reloading the
554        * settings and the UI. */
555       accounts_dialog_update_settings (dialog, settings);
556       g_object_unref (account);
557     }
558
559   gtk_widget_set_sensitive (priv->treeview, TRUE);
560   gtk_widget_set_sensitive (priv->button_add, TRUE);
561   gtk_widget_set_sensitive (priv->button_remove, TRUE);
562   gtk_widget_set_sensitive (priv->button_import, TRUE);
563
564   if (settings != NULL)
565     g_object_unref (settings);
566 }
567
568 static void
569 empathy_account_dialog_widget_cancelled_cb (
570     EmpathyAccountWidget *widget_object,
571     EmpathyAccountsDialog *dialog)
572 {
573   empathy_account_dialog_cancel (dialog);
574 }
575
576 static gboolean
577 accounts_dialog_has_valid_accounts (EmpathyAccountsDialog *dialog)
578 {
579   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
580   GtkTreeModel *model;
581   GtkTreeIter iter;
582   gboolean creating;
583
584   g_object_get (priv->setting_widget_object,
585       "creating-account", &creating, NULL);
586
587   if (!creating)
588     return TRUE;
589
590   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
591
592   if (gtk_tree_model_get_iter_first (model, &iter))
593     return gtk_tree_model_iter_next (model, &iter);
594
595   return FALSE;
596 }
597
598 static void
599 account_dialog_create_edit_params_dialog (EmpathyAccountsDialog *dialog)
600 {
601   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
602   EmpathyAccountSettings *settings;
603   GtkWidget *subdialog, *content, *content_area, *align;
604
605   settings = accounts_dialog_model_get_selected_settings (dialog);
606   if (settings == NULL)
607     return;
608
609   subdialog = gtk_dialog_new_with_buttons (_("Edit Connection Parameters"),
610       GTK_WINDOW (dialog),
611       GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
612       NULL, NULL);
613
614   priv->setting_widget_object =
615     empathy_account_widget_new_for_protocol (settings, FALSE);
616
617   g_object_add_weak_pointer (G_OBJECT (priv->setting_widget_object),
618       (gpointer *) &priv->setting_widget_object);
619
620   if (accounts_dialog_has_valid_accounts (dialog))
621     empathy_account_widget_set_other_accounts_exist (
622         priv->setting_widget_object, TRUE);
623
624   content = empathy_account_widget_get_widget (priv->setting_widget_object);
625
626   g_signal_connect (priv->setting_widget_object, "cancelled",
627           G_CALLBACK (empathy_account_dialog_widget_cancelled_cb), dialog);
628
629   g_signal_connect_swapped (priv->setting_widget_object, "close",
630       G_CALLBACK (gtk_widget_destroy), subdialog);
631
632   content_area = gtk_dialog_get_content_area (GTK_DIALOG (subdialog));
633
634   align = gtk_alignment_new (0.5, 0.5, 1, 1);
635   gtk_alignment_set_padding (GTK_ALIGNMENT (align), 6, 0, 6, 6);
636
637   gtk_container_add (GTK_CONTAINER (align), content);
638   gtk_box_pack_start (GTK_BOX (content_area), align, TRUE, TRUE, 0);
639
640   gtk_widget_show (content);
641   gtk_widget_show (align);
642   gtk_widget_show (subdialog);
643 }
644
645 static void
646 start_external_app (GAppInfo *app_info)
647 {
648   GError *error = NULL;
649   GdkAppLaunchContext *context = NULL;
650   GdkDisplay *display;
651
652   display = gdk_display_get_default ();
653   context = gdk_display_get_app_launch_context (display);
654
655   if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context,
656         &error))
657     {
658       g_critical ("Failed to bisho: %s", error->message);
659       g_clear_error (&error);
660     }
661
662   tp_clear_object (&context);
663 }
664
665 static void
666 use_external_storage_provider (EmpathyAccountsDialog *self,
667     TpAccount *account)
668 {
669   const gchar *provider;
670
671   provider = tp_account_get_storage_provider (account);
672   if (!tp_strdiff (provider, "com.meego.libsocialweb"))
673     {
674       GDesktopAppInfo *desktop_info;
675       gchar *cmd;
676       GAppInfo *app_info;
677       GError *error = NULL;
678
679       desktop_info = g_desktop_app_info_new ("gnome-control-center.desktop");
680       if (desktop_info == NULL)
681         {
682           g_critical ("Could not locate 'gnome-control-center.desktop'");
683           return;
684         }
685
686       /* glib doesn't have API to start a desktop file with args... (#637875) */
687       cmd = g_strdup_printf ("%s bisho.desktop", g_app_info_get_commandline (
688             (GAppInfo *) desktop_info));
689
690       app_info = g_app_info_create_from_commandline (cmd, NULL, 0, &error);
691
692       if (app_info == NULL)
693         {
694           DEBUG ("Failed to create app info: %s", error->message);
695           g_error_free (error);
696         }
697       else
698         {
699           start_external_app (app_info);
700           g_object_unref (app_info);
701         }
702
703       g_object_unref (desktop_info);
704       g_free (cmd);
705       return;
706     }
707   else if (!tp_strdiff (provider, "org.gnome.OnlineAccounts"))
708     {
709       GDesktopAppInfo *desktop_info;
710
711       desktop_info = g_desktop_app_info_new (
712           "gnome-online-accounts-panel.desktop");
713       if (desktop_info == NULL)
714         {
715           g_critical ("Could not locate 'gnome-online-accounts-panel.desktop'");
716         }
717       else
718         {
719           start_external_app (G_APP_INFO (desktop_info));
720           g_object_unref (desktop_info);
721         }
722
723       return;
724     }
725   else
726     {
727       DEBUG ("Don't know how to handle %s", provider);
728       return;
729     }
730 }
731
732 static void
733 account_dialow_show_edit_params_dialog (EmpathyAccountsDialog *dialog,
734     GtkButton *button)
735 {
736   EmpathyAccountSettings *settings;
737   TpAccount *account;
738   TpStorageRestrictionFlags storage_restrictions;
739
740   settings = accounts_dialog_model_get_selected_settings (dialog);
741   if (settings == NULL)
742     return;
743
744   account = empathy_account_settings_get_account (settings);
745   g_return_if_fail (account != NULL);
746
747   storage_restrictions = tp_account_get_storage_restrictions (account);
748
749   /* Empathy can only edit accounts without the Cannot_Set_Parameters flag */
750   if (storage_restrictions & TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS)
751     {
752       DEBUG ("Account is provided by an external storage provider");
753
754       use_external_storage_provider (dialog, account);
755     }
756   else
757     {
758       account_dialog_create_edit_params_dialog (dialog);
759     }
760 }
761
762 static void
763 account_dialog_show_contact_details_failed (EmpathyAccountsDialog *dialog,
764     gboolean error)
765 {
766   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
767   GtkWidget *infobar, *label;
768
769   infobar = gtk_info_bar_new ();
770
771   if (error)
772     {
773       gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), GTK_MESSAGE_ERROR);
774       label = gtk_label_new (_("Failed to retrieve your personal information "
775                                "from the server."));
776     }
777   else
778     {
779       gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), GTK_MESSAGE_INFO);
780       label = gtk_label_new (_("Go online to edit your personal information."));
781     }
782
783   gtk_container_add (
784       GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar))),
785       label);
786   gtk_box_pack_start (GTK_BOX (priv->dialog_content), infobar, FALSE, FALSE, 0);
787   gtk_widget_show_all (infobar);
788 }
789
790 static void
791 account_dialog_got_self_contact (TpConnection *conn,
792     EmpathyContact *contact,
793     const GError *in_error,
794     gpointer user_data,
795     GObject *dialog)
796 {
797   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
798   GtkWidget *editor, *alig;
799
800   if (in_error != NULL)
801     {
802       DEBUG ("Failed to get self-contact: %s", in_error->message);
803       account_dialog_show_contact_details_failed (
804           EMPATHY_ACCOUNTS_DIALOG (dialog), TRUE);
805       return;
806     }
807
808   alig = gtk_alignment_new (0.5, 0, 1, 1);
809
810   /* create the contact info editor for this account */
811   editor = empathy_contact_widget_new (contact,
812       EMPATHY_CONTACT_WIDGET_EDIT_ALIAS |
813       EMPATHY_CONTACT_WIDGET_EDIT_AVATAR |
814       EMPATHY_CONTACT_WIDGET_NO_STATUS |
815       EMPATHY_CONTACT_WIDGET_EDIT_DETAILS |
816       EMPATHY_CONTACT_WIDGET_NO_ACCOUNT);
817
818   gtk_box_pack_start (GTK_BOX (priv->dialog_content), alig, TRUE, TRUE, 0);
819   gtk_container_add (GTK_CONTAINER (alig), editor);
820   gtk_widget_show (alig);
821   gtk_widget_show (editor);
822 }
823
824 static void
825 account_dialog_create_dialog_content (EmpathyAccountsDialog *dialog,
826     EmpathyAccountSettings *settings)
827 {
828   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
829   const gchar *icon_name;
830   TpAccount *account;
831   TpConnection *conn = NULL;
832   GtkWidget *bbox, *button;
833
834   account = empathy_account_settings_get_account (settings);
835
836   // if (priv->setting_widget_object != NULL)
837   //   g_object_remove_weak_pointer (G_OBJECT (priv->setting_widget_object),
838   //       (gpointer *) &priv->setting_widget_object);
839
840   priv->dialog_content = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
841   gtk_container_add (GTK_CONTAINER (priv->alignment_settings),
842       priv->dialog_content);
843   gtk_widget_show (priv->dialog_content);
844
845   /* request the self contact */
846   if (account != NULL)
847     conn = tp_account_get_connection (account);
848
849   if (conn != NULL &&
850       tp_proxy_get_invalidated (conn) == NULL)
851     {
852       empathy_tp_contact_factory_get_from_handle (conn,
853           tp_connection_get_self_handle (conn),
854           account_dialog_got_self_contact,
855           NULL, NULL, G_OBJECT (dialog));
856     }
857   else
858     {
859       account_dialog_show_contact_details_failed (dialog, FALSE);
860     }
861
862   bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
863   gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
864   gtk_box_pack_end (GTK_BOX (priv->dialog_content), bbox, FALSE, TRUE, 0);
865   gtk_widget_show (bbox);
866
867   button = gtk_button_new_with_mnemonic (_("_Edit Connection Parameters..."));
868   gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, TRUE, 0);
869   gtk_widget_show (button);
870   g_signal_connect_swapped (button, "clicked",
871       G_CALLBACK (account_dialow_show_edit_params_dialog), dialog);
872
873   icon_name = empathy_account_settings_get_icon_name (settings);
874
875   if (!gtk_icon_theme_has_icon (gtk_icon_theme_get_default (),
876           icon_name))
877     /* show the default icon; keep this in sync with the default
878      * one in empathy-accounts-dialog.ui.
879      */
880     icon_name = GTK_STOCK_CUT;
881
882   gtk_image_set_from_icon_name (GTK_IMAGE (priv->image_type),
883       icon_name, GTK_ICON_SIZE_DIALOG);
884   gtk_widget_set_tooltip_text (priv->image_type,
885       empathy_protocol_name_to_display_name
886       (empathy_account_settings_get_protocol (settings)));
887   gtk_widget_show (priv->image_type);
888
889   accounts_dialog_update_name_label (dialog,
890       empathy_account_settings_get_display_name (settings));
891
892   accounts_dialog_update_status_infobar (dialog, account);
893 }
894
895 static void
896 account_dialog_settings_ready_cb (EmpathyAccountSettings *settings,
897     GParamSpec *spec,
898     EmpathyAccountsDialog *dialog)
899 {
900   if (empathy_account_settings_is_ready (settings))
901     account_dialog_create_dialog_content (dialog, settings);
902 }
903
904 static void
905 accounts_dialog_model_select_first (EmpathyAccountsDialog *dialog)
906 {
907   GtkTreeView      *view;
908   GtkTreeModel     *model;
909   GtkTreeSelection *selection;
910   GtkTreeIter       iter;
911   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
912
913   /* select first */
914   view = GTK_TREE_VIEW (priv->treeview);
915   model = gtk_tree_view_get_model (view);
916
917   if (gtk_tree_model_get_iter_first (model, &iter))
918     {
919       selection = gtk_tree_view_get_selection (view);
920       gtk_tree_selection_select_iter (selection, &iter);
921     }
922   else
923     {
924       accounts_dialog_update_settings (dialog, NULL);
925     }
926 }
927
928 static gboolean
929 accounts_dialog_has_pending_change (EmpathyAccountsDialog *dialog,
930     TpAccount **account)
931 {
932   GtkTreeIter iter;
933   GtkTreeModel *model;
934   GtkTreeSelection *selection;
935   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
936
937   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
938
939   if (gtk_tree_selection_get_selected (selection, &model, &iter))
940     gtk_tree_model_get (model, &iter, COL_ACCOUNT, account, -1);
941
942   return priv->setting_widget_object != NULL
943       && empathy_account_widget_contains_pending_changes (
944           priv->setting_widget_object);
945 }
946
947 static void
948 accounts_dialog_show_question_dialog (EmpathyAccountsDialog *dialog,
949     const gchar *primary_text,
950     const gchar *secondary_text,
951     GCallback response_callback,
952     gpointer user_data,
953     const gchar *first_button_text,
954     ...)
955 {
956   va_list button_args;
957   GtkWidget *message_dialog;
958   const gchar *button_text;
959
960   message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
961       GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
962       GTK_MESSAGE_QUESTION,
963       GTK_BUTTONS_NONE,
964       "%s", primary_text);
965
966   gtk_message_dialog_format_secondary_text (
967       GTK_MESSAGE_DIALOG (message_dialog), "%s", secondary_text);
968
969   va_start (button_args, first_button_text);
970   for (button_text = first_button_text;
971        button_text;
972        button_text = va_arg (button_args, const gchar *))
973     {
974       gint response_id;
975       response_id = va_arg (button_args, gint);
976
977       gtk_dialog_add_button (GTK_DIALOG (message_dialog), button_text,
978           response_id);
979     }
980   va_end (button_args);
981
982   g_signal_connect (message_dialog, "response", response_callback, user_data);
983
984   gtk_widget_show (message_dialog);
985 }
986
987 static gchar *
988 get_dialog_primary_text (TpAccount *account)
989 {
990   if (account != NULL)
991     {
992       /* Existing account */
993       return g_strdup_printf (PENDING_CHANGES_QUESTION_PRIMARY_TEXT,
994           tp_account_get_display_name (account));
995     }
996   else
997     {
998       /* Newly created account */
999       return g_strdup (UNSAVED_NEW_ACCOUNT_QUESTION_PRIMARY_TEXT);
1000     }
1001 }
1002
1003 static void
1004 accounts_dialog_button_add_clicked_cb (GtkWidget *button,
1005     EmpathyAccountsDialog *self)
1006 {
1007   GtkWidget *dialog;
1008   gint response;
1009
1010   dialog = empathy_new_account_dialog_new (GTK_WINDOW (self));
1011   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1012
1013   response = gtk_dialog_run (GTK_DIALOG (dialog));
1014
1015   if (response == GTK_RESPONSE_OK)
1016     {
1017       EmpathyAccountSettings *settings;
1018       TpAccount *account;
1019
1020       settings = empathy_new_account_dialog_get_settings (
1021           EMPATHY_NEW_ACCOUNT_DIALOG (dialog));
1022
1023       /* The newly created account has already been added by
1024        * accounts_dialog_account_validity_changed_cb so we just
1025        * have to select it. */
1026       account = empathy_account_settings_get_account (settings);
1027       accounts_dialog_model_set_selected (self, account);
1028     }
1029
1030   gtk_widget_destroy (dialog);
1031 }
1032
1033 static void
1034 accounts_dialog_update_settings (EmpathyAccountsDialog *dialog,
1035     EmpathyAccountSettings *settings)
1036 {
1037   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1038
1039   if (priv->settings_ready != NULL)
1040     {
1041       g_signal_handler_disconnect (priv->settings_ready,
1042           priv->settings_ready_id);
1043       priv->settings_ready = NULL;
1044       priv->settings_ready_id = 0;
1045     }
1046
1047   if (!settings)
1048     {
1049       GtkTreeView  *view;
1050       GtkTreeModel *model;
1051       GtkTreeSelection *selection;
1052
1053       view = GTK_TREE_VIEW (priv->treeview);
1054       model = gtk_tree_view_get_model (view);
1055       selection = gtk_tree_view_get_selection (view);
1056
1057       if (gtk_tree_model_iter_n_children (model, NULL) > 0)
1058         {
1059           /* We have configured accounts, select the first one if there
1060            * is no other account selected already. */
1061           if (!gtk_tree_selection_get_selected (selection, NULL, NULL))
1062             accounts_dialog_model_select_first (dialog);
1063
1064           return;
1065         }
1066
1067       /* No account and no profile, warn the user */
1068       gtk_widget_hide (priv->vbox_details);
1069       gtk_widget_set_sensitive (priv->button_add, FALSE);
1070
1071       gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook_account),
1072           NOTEBOOK_PAGE_NO_PROTOCOL);
1073       return;
1074     }
1075
1076   /* We have an account selected, destroy old settings and create a new
1077    * one for the account selected */
1078   gtk_widget_show (priv->vbox_details);
1079
1080   if (priv->dialog_content)
1081     {
1082       gtk_widget_destroy (priv->dialog_content);
1083       priv->dialog_content = NULL;
1084     }
1085
1086   if (empathy_account_settings_is_ready (settings))
1087     {
1088       account_dialog_create_dialog_content (dialog, settings);
1089     }
1090   else
1091     {
1092       priv->settings_ready = settings;
1093       priv->settings_ready_id =
1094         g_signal_connect (settings, "notify::ready",
1095             G_CALLBACK (account_dialog_settings_ready_cb), dialog);
1096     }
1097
1098 }
1099
1100 static void
1101 accounts_dialog_name_editing_started_cb (GtkCellRenderer *renderer,
1102     GtkCellEditable *editable,
1103     gchar *path,
1104     EmpathyAccountsDialog *dialog)
1105 {
1106   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1107
1108   if (priv->connecting_id)
1109     g_source_remove (priv->connecting_id);
1110
1111   DEBUG ("Editing account name started; stopping flashing");
1112 }
1113
1114 static const gchar *
1115 get_status_icon_for_account (EmpathyAccountsDialog *self,
1116     TpAccount *account)
1117 {
1118   EmpathyAccountsDialogPriv *priv = GET_PRIV (self);
1119   TpConnectionStatus status;
1120   TpConnectionStatusReason reason;
1121   TpConnectionPresenceType presence;
1122
1123   if (account == NULL)
1124     return empathy_icon_name_for_presence (TP_CONNECTION_PRESENCE_TYPE_OFFLINE);
1125
1126   if (!tp_account_is_enabled (account))
1127     return empathy_icon_name_for_presence (TP_CONNECTION_PRESENCE_TYPE_OFFLINE);
1128
1129   status = tp_account_get_connection_status (account, &reason);
1130
1131   if (status == TP_CONNECTION_STATUS_DISCONNECTED)
1132     {
1133       if (reason != TP_CONNECTION_STATUS_REASON_REQUESTED)
1134         /* An error occured */
1135         return GTK_STOCK_DIALOG_ERROR;
1136
1137       presence = TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
1138     }
1139   else if (status == TP_CONNECTION_STATUS_CONNECTING)
1140     {
1141       /* Account is connecting. Display a blinking account alternating between
1142        * the offline icon and the requested presence. */
1143       if (priv->connecting_show)
1144         presence = tp_account_get_requested_presence (account, NULL, NULL);
1145       else
1146         presence = TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
1147     }
1148   else
1149     {
1150       /* status == TP_CONNECTION_STATUS_CONNECTED */
1151       presence = tp_account_get_current_presence (account, NULL, NULL);
1152
1153       /* If presence is Unset (CM doesn't implement SimplePresence),
1154        * display the 'available' icon.
1155        * We also check Offline because of this MC5 bug: fd.o #26060 */
1156       if (presence == TP_CONNECTION_PRESENCE_TYPE_OFFLINE ||
1157           presence == TP_CONNECTION_PRESENCE_TYPE_UNSET)
1158         presence = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
1159     }
1160
1161   return empathy_icon_name_for_presence (presence);
1162 }
1163
1164 static void
1165 accounts_dialog_model_status_pixbuf_data_func (GtkTreeViewColumn *tree_column,
1166     GtkCellRenderer *cell,
1167     GtkTreeModel *model,
1168     GtkTreeIter *iter,
1169     EmpathyAccountsDialog *dialog)
1170 {
1171   TpAccount *account;
1172
1173   gtk_tree_model_get (model, iter, COL_ACCOUNT, &account, -1);
1174
1175   g_object_set (cell,
1176       "icon-name", get_status_icon_for_account (dialog, account),
1177       NULL);
1178
1179   if (account != NULL)
1180     g_object_unref (account);
1181 }
1182
1183 static void
1184 accounts_dialog_model_protocol_pixbuf_data_func (GtkTreeViewColumn *tree_column,
1185     GtkCellRenderer *cell,
1186     GtkTreeModel *model,
1187     GtkTreeIter *iter,
1188     EmpathyAccountsDialog *dialog)
1189 {
1190   EmpathyAccountSettings  *settings;
1191   gchar              *icon_name;
1192   GdkPixbuf          *pixbuf;
1193   TpConnectionStatus  status;
1194
1195   gtk_tree_model_get (model, iter,
1196       COL_STATUS, &status,
1197       COL_ACCOUNT_SETTINGS, &settings,
1198       -1);
1199
1200   icon_name = empathy_account_settings_get_icon_name (settings);
1201   pixbuf = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON);
1202
1203   g_object_set (cell,
1204       "visible", TRUE,
1205       "pixbuf", pixbuf,
1206       NULL);
1207
1208   g_object_unref (settings);
1209
1210   if (pixbuf)
1211     g_object_unref (pixbuf);
1212 }
1213
1214 static gboolean
1215 accounts_dialog_row_changed_foreach (GtkTreeModel *model,
1216     GtkTreePath *path,
1217     GtkTreeIter *iter,
1218     gpointer user_data)
1219 {
1220   TpAccount *account;
1221
1222   gtk_tree_model_get (model, iter, COL_ACCOUNT, &account, -1);
1223
1224   if (account == NULL)
1225     return FALSE;
1226
1227   if (tp_account_get_connection_status (account, NULL) ==
1228       TP_CONNECTION_STATUS_CONNECTING)
1229     {
1230       /* Only update the row where we have a connecting account as that's the
1231        * ones having a blinking icon. */
1232       gtk_tree_model_row_changed (model, path, iter);
1233     }
1234
1235   g_object_unref (account);
1236   return FALSE;
1237 }
1238
1239 static gboolean
1240 accounts_dialog_flash_connecting_cb (EmpathyAccountsDialog *dialog)
1241 {
1242   GtkTreeView  *view;
1243   GtkTreeModel *model;
1244   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1245
1246   priv->connecting_show = !priv->connecting_show;
1247
1248   view = GTK_TREE_VIEW (priv->treeview);
1249   model = gtk_tree_view_get_model (view);
1250
1251   gtk_tree_model_foreach (model, accounts_dialog_row_changed_foreach, NULL);
1252
1253   return TRUE;
1254 }
1255
1256 static void
1257 accounts_dialog_name_edited_cb (GtkCellRendererText *renderer,
1258     gchar *path,
1259     gchar *new_text,
1260     EmpathyAccountsDialog *dialog)
1261 {
1262   EmpathyAccountSettings    *settings;
1263   GtkTreeModel *model;
1264   GtkTreePath  *treepath;
1265   GtkTreeIter   iter;
1266   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1267   gboolean connecting;
1268
1269   empathy_account_manager_get_accounts_connected (&connecting);
1270
1271   if (connecting)
1272     {
1273       priv->connecting_id = g_timeout_add (FLASH_TIMEOUT,
1274           (GSourceFunc) accounts_dialog_flash_connecting_cb,
1275           dialog);
1276     }
1277
1278   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1279   treepath = gtk_tree_path_new_from_string (path);
1280   gtk_tree_model_get_iter (model, &iter, treepath);
1281   gtk_tree_model_get (model, &iter,
1282       COL_ACCOUNT_SETTINGS, &settings,
1283       -1);
1284   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1285       COL_NAME, new_text,
1286       -1);
1287   gtk_tree_path_free (treepath);
1288
1289   empathy_account_settings_set_display_name_async (settings, new_text,
1290       NULL, NULL);
1291   g_object_set (settings, "display-name-overridden", TRUE, NULL);
1292   g_object_unref (settings);
1293 }
1294
1295 static void
1296 accounts_dialog_delete_account_response_cb (GtkDialog *message_dialog,
1297   gint response_id,
1298   gpointer user_data)
1299 {
1300   TpAccount *account;
1301   GtkTreeModel *model;
1302   GtkTreeIter iter;
1303   GtkTreeSelection *selection;
1304   EmpathyAccountsDialog *account_dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1305   EmpathyAccountsDialogPriv *priv = GET_PRIV (account_dialog);
1306
1307   if (response_id == GTK_RESPONSE_YES)
1308     {
1309       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1310
1311       if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1312         return;
1313
1314       gtk_tree_model_get (model, &iter, COL_ACCOUNT, &account, -1);
1315
1316       if (account != NULL)
1317         {
1318           tp_account_remove_async (account, NULL, NULL);
1319           g_object_unref (account);
1320           account = NULL;
1321         }
1322
1323       /* No need to call accounts_dialog_model_selection_changed while
1324        * removing as we are going to call accounts_dialog_model_select_first
1325        * right after which will update the selection. */
1326       g_signal_handlers_block_by_func (selection,
1327           accounts_dialog_model_selection_changed, account_dialog);
1328
1329       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1330
1331       g_signal_handlers_unblock_by_func (selection,
1332           accounts_dialog_model_selection_changed, account_dialog);
1333
1334       accounts_dialog_model_select_first (account_dialog);
1335     }
1336
1337   gtk_widget_destroy (GTK_WIDGET (message_dialog));
1338 }
1339
1340 static void
1341 accounts_dialog_remove_account_iter (EmpathyAccountsDialog *dialog,
1342     GtkTreeIter *iter)
1343 {
1344   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1345   TpAccount *account;
1346   GtkTreeModel *model;
1347   gchar *question_dialog_primary_text;
1348
1349   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1350
1351   gtk_tree_model_get (model, iter, COL_ACCOUNT, &account, -1);
1352
1353   if (account == NULL || !tp_account_is_valid (account))
1354     {
1355       if (account != NULL)
1356         g_object_unref (account);
1357       gtk_list_store_remove (GTK_LIST_STORE (model), iter);
1358       accounts_dialog_model_select_first (dialog);
1359       return;
1360     }
1361
1362   question_dialog_primary_text = g_strdup_printf (
1363       _("Do you want to remove %s from your computer?"),
1364       tp_account_get_display_name (account));
1365
1366   accounts_dialog_show_question_dialog (dialog, question_dialog_primary_text,
1367       _("This will not remove your account on the server."),
1368       G_CALLBACK (accounts_dialog_delete_account_response_cb),
1369       dialog,
1370       GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1371       GTK_STOCK_REMOVE, GTK_RESPONSE_YES, NULL);
1372
1373   g_free (question_dialog_primary_text);
1374   g_object_unref (account);
1375 }
1376
1377 static void
1378 accounts_dialog_button_remove_clicked_cb (GtkWidget *button,
1379     EmpathyAccountsDialog *dialog)
1380 {
1381   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1382   GtkTreeView  *view;
1383   GtkTreeSelection *selection;
1384   GtkTreeIter iter;
1385
1386   view = GTK_TREE_VIEW (priv->treeview);
1387   selection = gtk_tree_view_get_selection (view);
1388   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
1389       return;
1390
1391   accounts_dialog_remove_account_iter (dialog, &iter);
1392 }
1393
1394 #ifdef HAVE_MEEGO
1395 static void
1396 accounts_dialog_view_delete_activated_cb (EmpathyCellRendererActivatable *cell,
1397     const gchar *path_string,
1398     EmpathyAccountsDialog *dialog)
1399 {
1400   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1401   GtkTreeModel *model;
1402   GtkTreeIter iter;
1403
1404   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1405   if (!gtk_tree_model_get_iter_from_string (model, &iter, path_string))
1406     return;
1407
1408   accounts_dialog_remove_account_iter (dialog, &iter);
1409 }
1410 #endif /* HAVE_MEEGO */
1411
1412 static void
1413 accounts_dialog_model_add_columns (EmpathyAccountsDialog *dialog)
1414 {
1415   GtkTreeView       *view;
1416   GtkTreeViewColumn *column;
1417   GtkCellRenderer   *cell;
1418   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1419
1420   view = GTK_TREE_VIEW (priv->treeview);
1421   gtk_tree_view_set_headers_visible (view, FALSE);
1422
1423   /* Account column */
1424   column = gtk_tree_view_column_new ();
1425   gtk_tree_view_column_set_expand (column, TRUE);
1426   gtk_tree_view_append_column (view, column);
1427
1428   /* Status icon renderer */
1429   cell = gtk_cell_renderer_pixbuf_new ();
1430   gtk_tree_view_column_pack_start (column, cell, FALSE);
1431   gtk_tree_view_column_set_cell_data_func (column, cell,
1432       (GtkTreeCellDataFunc)
1433       accounts_dialog_model_status_pixbuf_data_func,
1434       dialog,
1435       NULL);
1436
1437   /* Protocol icon renderer */
1438   cell = gtk_cell_renderer_pixbuf_new ();
1439   gtk_tree_view_column_pack_start (column, cell, FALSE);
1440   gtk_tree_view_column_set_cell_data_func (column, cell,
1441       (GtkTreeCellDataFunc)
1442       accounts_dialog_model_protocol_pixbuf_data_func,
1443       dialog,
1444       NULL);
1445
1446   /* Name renderer */
1447   priv->name_renderer = gtk_cell_renderer_text_new ();
1448   g_object_set (priv->name_renderer,
1449       "ellipsize", PANGO_ELLIPSIZE_END,
1450       "width-chars", 25,
1451       "editable", TRUE,
1452       NULL);
1453   gtk_tree_view_column_pack_start (column, priv->name_renderer, TRUE);
1454   gtk_tree_view_column_add_attribute (column, priv->name_renderer,
1455       "text", COL_NAME);
1456   g_signal_connect (priv->name_renderer, "edited",
1457       G_CALLBACK (accounts_dialog_name_edited_cb),
1458       dialog);
1459   g_signal_connect (priv->name_renderer, "editing-started",
1460       G_CALLBACK (accounts_dialog_name_editing_started_cb),
1461       dialog);
1462   g_object_set (priv->name_renderer, "ypad", 4, NULL);
1463
1464 #ifdef HAVE_MEEGO
1465   /* Delete column */
1466   cell = empathy_cell_renderer_activatable_new ();
1467   gtk_tree_view_column_pack_start (column, cell, FALSE);
1468   g_object_set (cell,
1469         "icon-name", GTK_STOCK_DELETE,
1470         "show-on-select", TRUE,
1471         NULL);
1472
1473   g_signal_connect (cell, "path-activated",
1474       G_CALLBACK (accounts_dialog_view_delete_activated_cb),
1475       dialog);
1476 #endif /* HAVE_MEEGO */
1477 }
1478
1479 static EmpathyAccountSettings *
1480 accounts_dialog_model_get_selected_settings (EmpathyAccountsDialog *dialog)
1481 {
1482   GtkTreeView      *view;
1483   GtkTreeModel     *model;
1484   GtkTreeSelection *selection;
1485   GtkTreeIter       iter;
1486   EmpathyAccountSettings   *settings;
1487   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1488
1489   view = GTK_TREE_VIEW (priv->treeview);
1490   selection = gtk_tree_view_get_selection (view);
1491
1492   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1493     return NULL;
1494
1495   gtk_tree_model_get (model, &iter,
1496       COL_ACCOUNT_SETTINGS, &settings, -1);
1497
1498   return settings;
1499 }
1500
1501 static void
1502 accounts_dialog_model_selection_changed (GtkTreeSelection *selection,
1503     EmpathyAccountsDialog *dialog)
1504 {
1505   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1506   EmpathyAccountSettings *settings;
1507   GtkTreeModel *model;
1508   GtkTreeIter   iter;
1509   gboolean      is_selection;
1510   gboolean creating = FALSE;
1511
1512   is_selection = gtk_tree_selection_get_selected (selection, &model, &iter);
1513
1514   settings = accounts_dialog_model_get_selected_settings (dialog);
1515   accounts_dialog_update_settings (dialog, settings);
1516
1517   if (settings != NULL)
1518     g_object_unref (settings);
1519
1520   if (priv->setting_widget_object != NULL)
1521     {
1522       g_object_get (priv->setting_widget_object,
1523           "creating-account", &creating, NULL);
1524     }
1525
1526   /* Update remove button sensitivity */
1527   gtk_widget_set_sensitive (priv->button_remove, is_selection && !creating &&
1528       !priv->loading);
1529 }
1530
1531 static void
1532 accounts_dialog_selection_change_response_cb (GtkDialog *message_dialog,
1533   gint response_id,
1534   gpointer *user_data)
1535 {
1536   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1537   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1538
1539   gtk_widget_destroy (GTK_WIDGET (message_dialog));
1540
1541     if (response_id == GTK_RESPONSE_YES && priv->destination_row != NULL)
1542       {
1543         /* The user wants to lose unsaved changes to the currently selected
1544          * account and select another account. We discard the changes and
1545          * select the other account. */
1546         GtkTreePath *path;
1547         GtkTreeSelection *selection;
1548
1549         priv->force_change_row = TRUE;
1550         empathy_account_widget_discard_pending_changes (
1551             priv->setting_widget_object);
1552
1553         path = gtk_tree_row_reference_get_path (priv->destination_row);
1554         selection = gtk_tree_view_get_selection (
1555             GTK_TREE_VIEW (priv->treeview));
1556
1557         if (path != NULL)
1558           {
1559             /* This will trigger a call to
1560              * accounts_dialog_account_selection_change() */
1561             gtk_tree_selection_select_path (selection, path);
1562             gtk_tree_path_free (path);
1563           }
1564
1565         gtk_tree_row_reference_free (priv->destination_row);
1566       }
1567     else
1568       {
1569         priv->force_change_row = FALSE;
1570       }
1571 }
1572
1573 static gboolean
1574 accounts_dialog_account_selection_change (GtkTreeSelection *selection,
1575     GtkTreeModel *model,
1576     GtkTreePath *path,
1577     gboolean path_currently_selected,
1578     gpointer data)
1579 {
1580   TpAccount *account = NULL;
1581   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (data);
1582   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1583   gboolean ret;
1584
1585   if (priv->force_change_row)
1586     {
1587       /* We came back here because the user wants to discard changes to his
1588        * modified account. The changes have already been discarded so we
1589        * just change the selected row. */
1590       priv->force_change_row = FALSE;
1591       return TRUE;
1592     }
1593
1594   if (accounts_dialog_has_pending_change (dialog, &account))
1595     {
1596       /* The currently selected account has some unsaved changes. We ask
1597        * the user if he really wants to lose his changes and select another
1598        * account */
1599       gchar *question_dialog_primary_text = get_dialog_primary_text (account);
1600       priv->destination_row = gtk_tree_row_reference_new (model, path);
1601
1602       accounts_dialog_show_question_dialog (dialog,
1603           question_dialog_primary_text,
1604           _("You are about to select another account, which will discard\n"
1605               "your changes. Are you sure you want to proceed?"),
1606           G_CALLBACK (accounts_dialog_selection_change_response_cb),
1607           dialog,
1608           GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1609           GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
1610
1611       g_free (question_dialog_primary_text);
1612       ret = FALSE;
1613     }
1614   else
1615     {
1616       ret = TRUE;
1617     }
1618
1619   if (account != NULL)
1620     g_object_unref (account);
1621
1622   return ret;
1623 }
1624
1625 static void
1626 accounts_dialog_model_setup (EmpathyAccountsDialog *dialog)
1627 {
1628   GtkListStore     *store;
1629   GtkTreeSelection *selection;
1630   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1631
1632   store = gtk_list_store_new (COL_COUNT,
1633       G_TYPE_STRING,         /* name */
1634       G_TYPE_UINT,           /* status */
1635       TP_TYPE_ACCOUNT,   /* account */
1636       EMPATHY_TYPE_ACCOUNT_SETTINGS); /* settings */
1637
1638   gtk_tree_view_set_model (GTK_TREE_VIEW (priv->treeview),
1639       GTK_TREE_MODEL (store));
1640
1641   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1642   gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
1643   gtk_tree_selection_set_select_function (selection,
1644       accounts_dialog_account_selection_change, dialog, NULL);
1645
1646   g_signal_connect (selection, "changed",
1647       G_CALLBACK (accounts_dialog_model_selection_changed),
1648       dialog);
1649
1650   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
1651       COL_NAME, GTK_SORT_ASCENDING);
1652
1653   accounts_dialog_model_add_columns (dialog);
1654
1655   g_object_unref (store);
1656 }
1657
1658 static gboolean
1659 accounts_dialog_get_account_iter (EmpathyAccountsDialog *dialog,
1660     TpAccount *account,
1661     GtkTreeIter *iter)
1662 {
1663   GtkTreeView      *view;
1664   GtkTreeModel     *model;
1665   gboolean          ok;
1666   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1667
1668   /* Update the status in the model */
1669   view = GTK_TREE_VIEW (priv->treeview);
1670   model = gtk_tree_view_get_model (view);
1671
1672   for (ok = gtk_tree_model_get_iter_first (model, iter);
1673        ok;
1674        ok = gtk_tree_model_iter_next (model, iter))
1675     {
1676       TpAccount *this_account;
1677       gboolean   equal;
1678
1679       gtk_tree_model_get (model, iter,
1680           COL_ACCOUNT, &this_account,
1681           -1);
1682
1683       equal = (this_account == account);
1684       g_object_unref (this_account);
1685
1686       if (equal)
1687         return TRUE;
1688     }
1689
1690   return FALSE;
1691 }
1692
1693 static void
1694 select_and_scroll_to_iter (EmpathyAccountsDialog *dialog,
1695     GtkTreeIter *iter)
1696 {
1697   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1698   GtkTreeSelection *selection;
1699   GtkTreePath *path;
1700   GtkTreeModel *model;
1701
1702   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1703
1704   gtk_tree_selection_select_iter (selection, iter);
1705
1706   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1707   path = gtk_tree_model_get_path (model, iter);
1708
1709   gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->treeview), path, NULL,
1710       TRUE, 0, 0.5);
1711
1712   gtk_tree_path_free (path);
1713 }
1714
1715 static void
1716 accounts_dialog_model_set_selected (EmpathyAccountsDialog *dialog,
1717     TpAccount *account)
1718 {
1719   GtkTreeIter       iter;
1720
1721   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1722     select_and_scroll_to_iter (dialog, &iter);
1723 }
1724
1725 static void
1726 accounts_dialog_treeview_enabled_cb (GtkMenuItem *item,
1727     TpAccount *account)
1728 {
1729   gboolean enabled;
1730
1731   enabled = tp_account_is_enabled (account);
1732
1733   enable_and_connect_account (account, !enabled);
1734 }
1735
1736 static gboolean
1737 accounts_dialog_treeview_button_press_event_cb (GtkTreeView *view,
1738     GdkEventButton *event,
1739     EmpathyAccountsDialog *dialog)
1740 {
1741   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1742   TpAccount *account = NULL;
1743   GtkTreeModel *model = NULL;
1744   GtkTreePath *path = NULL;
1745   GtkTreeIter iter;
1746   GtkWidget *menu;
1747   GtkWidget *item;
1748
1749   /* ignore multiple clicks */
1750   if (event->type != GDK_BUTTON_PRESS)
1751     return TRUE;
1752
1753   if (event->button != 3)
1754     goto finally;
1755
1756   /* Selection is not yet set, so we have to get account from event position */
1757   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1758   if (!gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->treeview),
1759       event->x, event->y, &path, NULL, NULL, NULL))
1760     goto finally;
1761
1762   if (!gtk_tree_model_get_iter (model, &iter, path))
1763     goto finally;
1764
1765   gtk_tree_model_get (model, &iter, COL_ACCOUNT, &account, -1);
1766
1767   /* Create the menu */
1768   menu = empathy_context_menu_new (GTK_WIDGET (view));
1769
1770   /* Menu item: to enabled/disable the account */
1771   item = gtk_check_menu_item_new_with_mnemonic (_("_Enabled"));
1772
1773   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1774
1775   if (account_can_be_enabled (account))
1776     {
1777       gboolean active;
1778
1779       active = tp_account_is_enabled (account);
1780       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
1781           active);
1782
1783       tp_g_signal_connect_object (item, "activate",
1784           G_CALLBACK (accounts_dialog_treeview_enabled_cb), account, 0);
1785     }
1786   else
1787     {
1788       gtk_widget_set_sensitive (item, FALSE);
1789     }
1790
1791   gtk_widget_show (item);
1792
1793   /* FIXME: Add here presence items, to be able to set per-account presence */
1794
1795   /* Popup menu */
1796   gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
1797       event->button, event->time);
1798
1799 finally:
1800   tp_clear_object (&account);
1801   gtk_tree_path_free (path);
1802
1803   return FALSE;
1804 }
1805
1806 static void
1807 reload_account_widget (EmpathyAccountsDialog *self)
1808 {
1809   EmpathyAccountSettings *settings;
1810
1811   settings = accounts_dialog_model_get_selected_settings (self);
1812   accounts_dialog_update_settings (self, settings);
1813 }
1814
1815 static void
1816 accounts_dialog_connection_changed_cb (TpAccount *account,
1817     guint old_status,
1818     guint current,
1819     guint reason,
1820     gchar *dbus_error_name,
1821     GHashTable *details,
1822     EmpathyAccountsDialog *dialog)
1823 {
1824   GtkTreeModel *model;
1825   GtkTreeIter   iter;
1826   gboolean      found;
1827   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1828
1829   /* Update the status-infobar in the details view */
1830   accounts_dialog_update_status_infobar (dialog, account);
1831
1832   /* Update the status in the model */
1833   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1834
1835   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1836     {
1837       GtkTreePath *path;
1838
1839       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1840           COL_STATUS, current,
1841           -1);
1842
1843       path = gtk_tree_model_get_path (model, &iter);
1844       gtk_tree_model_row_changed (model, path, &iter);
1845       gtk_tree_path_free (path);
1846     }
1847
1848   empathy_account_manager_get_accounts_connected (&found);
1849
1850   if (!found && priv->connecting_id)
1851     {
1852       g_source_remove (priv->connecting_id);
1853       priv->connecting_id = 0;
1854     }
1855
1856   if (found && !priv->connecting_id)
1857     priv->connecting_id = g_timeout_add (FLASH_TIMEOUT,
1858         (GSourceFunc) accounts_dialog_flash_connecting_cb,
1859         dialog);
1860 }
1861
1862 static void
1863 update_account_in_treeview (EmpathyAccountsDialog *self,
1864     TpAccount *account)
1865 {
1866   EmpathyAccountsDialogPriv *priv = GET_PRIV (self);
1867   GtkTreeIter iter;
1868   GtkTreeModel *model;
1869
1870   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1871   if (accounts_dialog_get_account_iter (self, account, &iter))
1872     {
1873       GtkTreePath *path;
1874
1875       path = gtk_tree_model_get_path (model, &iter);
1876       gtk_tree_model_row_changed (model, path, &iter);
1877       gtk_tree_path_free (path);
1878     }
1879 }
1880
1881 static void
1882 accounts_dialog_presence_changed_cb (TpAccount *account,
1883     guint presence,
1884     gchar *status,
1885     gchar *status_message,
1886     EmpathyAccountsDialog *dialog)
1887 {
1888   /* Update the status-infobar in the details view */
1889   accounts_dialog_update_status_infobar (dialog, account);
1890
1891   update_account_in_treeview (dialog, account);
1892 }
1893
1894 static void
1895 accounts_dialog_account_display_name_changed_cb (TpAccount *account,
1896   GParamSpec *pspec,
1897   gpointer user_data)
1898 {
1899   const gchar *display_name;
1900   GtkTreeIter iter;
1901   GtkTreeModel *model;
1902   EmpathyAccountSettings *settings;
1903   TpAccount *selected_account;
1904   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1905   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1906
1907   display_name = tp_account_get_display_name (account);
1908   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1909   settings = accounts_dialog_model_get_selected_settings (dialog);
1910   if (settings == NULL)
1911     return;
1912
1913   selected_account = empathy_account_settings_get_account (settings);
1914
1915   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1916     {
1917       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1918           COL_NAME, display_name,
1919           -1);
1920     }
1921
1922   if (selected_account == account)
1923     accounts_dialog_update_name_label (dialog, display_name);
1924
1925   g_object_unref (settings);
1926 }
1927
1928 static void
1929 conn_prepare_cb (GObject *source,
1930     GAsyncResult *result,
1931     gpointer user_data)
1932 {
1933   EmpathyAccountsDialog *self = user_data;
1934
1935   reload_account_widget (self);
1936 }
1937
1938 static void
1939 accounts_dialog_notify_connection_cb (TpAccount *account,
1940     GParamSpec *spec,
1941     EmpathyAccountsDialog *self)
1942 {
1943   TpConnection *conn;
1944   if (!account_is_selected (self, account))
1945     return;
1946
1947   conn = tp_account_get_connection (account);
1948   if (conn == NULL)
1949     {
1950       reload_account_widget (self);
1951     }
1952   else
1953     {
1954       /* Wait for this feature so TpConnection will have fetch the
1955        * self handle */
1956       GQuark features[] = { TP_CONNECTION_FEATURE_CONNECTED, 0 };
1957
1958       tp_proxy_prepare_async (conn, features, conn_prepare_cb, self);
1959     }
1960 }
1961
1962 static void
1963 accounts_dialog_add_account (EmpathyAccountsDialog *dialog,
1964     TpAccount *account)
1965 {
1966   EmpathyAccountSettings *settings;
1967   GtkTreeModel       *model;
1968   GtkTreeIter         iter;
1969   TpConnectionStatus  status;
1970   const gchar        *name;
1971   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1972   gboolean selected = FALSE;
1973   GtkTreeSelection *selection;
1974
1975   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1976   status = tp_account_get_connection_status (account, NULL);
1977   name = tp_account_get_display_name (account);
1978
1979   settings = empathy_account_settings_new_for_account (account);
1980
1981   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1982
1983   if (!accounts_dialog_get_account_iter (dialog, account, &iter))
1984     {
1985       /* Select the account if it's the first added */
1986       if (gtk_tree_selection_count_selected_rows (selection) == 0)
1987         selected = TRUE;
1988
1989       gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter, -1,
1990           COL_NAME, name,
1991           COL_STATUS, status,
1992           COL_ACCOUNT, account,
1993           COL_ACCOUNT_SETTINGS, settings,
1994           -1);
1995     }
1996   else
1997     {
1998       selected = gtk_tree_selection_iter_is_selected (selection, &iter);
1999
2000       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2001           COL_NAME, name,
2002           COL_STATUS, status,
2003           COL_ACCOUNT, account,
2004           COL_ACCOUNT_SETTINGS, settings,
2005           -1);
2006     }
2007
2008   if (selected)
2009     {
2010       /* We just modified the selected account. Its display name may have been
2011        * changed and so it's place in the treeview. Scroll to it so it stays
2012        * visible. */
2013       select_and_scroll_to_iter (dialog, &iter);
2014     }
2015
2016   accounts_dialog_connection_changed_cb (account,
2017       0,
2018       status,
2019       TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED,
2020       NULL,
2021       NULL,
2022       dialog);
2023
2024   tp_g_signal_connect_object (account, "notify::display-name",
2025       G_CALLBACK (accounts_dialog_account_display_name_changed_cb),
2026       dialog, 0);
2027
2028   tp_g_signal_connect_object (account, "status-changed",
2029       G_CALLBACK (accounts_dialog_connection_changed_cb), dialog, 0);
2030   tp_g_signal_connect_object (account, "presence-changed",
2031       G_CALLBACK (accounts_dialog_presence_changed_cb), dialog, 0);
2032   tp_g_signal_connect_object (account, "notify::connection",
2033       G_CALLBACK (accounts_dialog_notify_connection_cb), dialog, 0);
2034
2035   g_object_unref (settings);
2036 }
2037
2038 static void
2039 accounts_dialog_account_validity_changed_cb (TpAccountManager *manager,
2040     TpAccount *account,
2041     gboolean valid,
2042     EmpathyAccountsDialog *dialog)
2043 {
2044   accounts_dialog_add_account (dialog, account);
2045 }
2046
2047 static void
2048 accounts_dialog_accounts_model_row_inserted_cb (GtkTreeModel *model,
2049     GtkTreePath *path,
2050     GtkTreeIter *iter,
2051     EmpathyAccountsDialog *dialog)
2052 {
2053   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2054
2055   if (priv->setting_widget_object != NULL &&
2056       accounts_dialog_has_valid_accounts (dialog))
2057     {
2058       empathy_account_widget_set_other_accounts_exist (
2059           priv->setting_widget_object, TRUE);
2060     }
2061 }
2062
2063 static void
2064 accounts_dialog_accounts_model_row_deleted_cb (GtkTreeModel *model,
2065     GtkTreePath *path,
2066     EmpathyAccountsDialog *dialog)
2067 {
2068   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2069
2070   if (priv->setting_widget_object != NULL &&
2071       !accounts_dialog_has_valid_accounts (dialog))
2072     {
2073       empathy_account_widget_set_other_accounts_exist (
2074           priv->setting_widget_object, FALSE);
2075     }
2076 }
2077
2078 static void
2079 accounts_dialog_account_removed_cb (TpAccountManager *manager,
2080     TpAccount *account,
2081     EmpathyAccountsDialog *dialog)
2082 {
2083   GtkTreeIter iter;
2084   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2085
2086   if (accounts_dialog_get_account_iter (dialog, account, &iter))
2087     {
2088       gtk_list_store_remove (GTK_LIST_STORE (
2089           gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview))), &iter);
2090     }
2091 }
2092
2093 static void
2094 enable_or_disable_account (EmpathyAccountsDialog *dialog,
2095     TpAccount *account,
2096     gboolean enabled)
2097 {
2098   /* Update the status-infobar in the details view */
2099   accounts_dialog_update_status_infobar (dialog, account);
2100
2101   DEBUG ("Account %s is now %s",
2102       tp_account_get_display_name (account),
2103       enabled ? "enabled" : "disabled");
2104 }
2105
2106 static void
2107 accounts_dialog_account_disabled_cb (TpAccountManager *manager,
2108     TpAccount *account,
2109     EmpathyAccountsDialog *dialog)
2110 {
2111   enable_or_disable_account (dialog, account, FALSE);
2112   update_account_in_treeview (dialog, account);
2113 }
2114
2115 static void
2116 accounts_dialog_account_enabled_cb (TpAccountManager *manager,
2117     TpAccount *account,
2118     EmpathyAccountsDialog *dialog)
2119 {
2120   enable_or_disable_account (dialog, account, TRUE);
2121 }
2122
2123 static GtkWidget *
2124 display_import_dialog (EmpathyAccountsDialog *dialog)
2125 {
2126   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2127   GtkWidget *import_dialog;
2128
2129   import_dialog = empathy_import_dialog_new (GTK_WINDOW (dialog),
2130       FALSE, priv->cms);
2131   gtk_widget_show (import_dialog);
2132
2133   return import_dialog;
2134 }
2135
2136 static void
2137 accounts_dialog_button_import_clicked_cb (GtkWidget *button,
2138     EmpathyAccountsDialog *dialog)
2139 {
2140   display_import_dialog (dialog);
2141 }
2142
2143 static void
2144 accounts_dialog_close_response_cb (GtkDialog *message_dialog,
2145   gint response_id,
2146   gpointer user_data)
2147 {
2148   GtkWidget *account_dialog = GTK_WIDGET (user_data);
2149
2150   gtk_widget_destroy (GTK_WIDGET (message_dialog));
2151
2152   if (response_id == GTK_RESPONSE_YES)
2153     gtk_widget_destroy (account_dialog);
2154 }
2155
2156 static gboolean
2157 accounts_dialog_delete_event_cb (GtkWidget *widget,
2158     GdkEvent *event,
2159     EmpathyAccountsDialog *dialog)
2160 {
2161   /* we maunally handle responses to delete events */
2162   return TRUE;
2163 }
2164
2165 static void
2166 accounts_dialog_set_selected_account (EmpathyAccountsDialog *dialog,
2167     TpAccount *account)
2168 {
2169   GtkTreeIter       iter;
2170
2171   if (accounts_dialog_get_account_iter (dialog, account, &iter))
2172     select_and_scroll_to_iter (dialog, &iter);
2173 }
2174
2175 static void
2176 salut_valid_cb (GtkWidget *widget,
2177     gboolean valid,
2178     GtkWidget *button)
2179 {
2180   gtk_widget_set_sensitive (button, valid);
2181 }
2182
2183 static void
2184 maybe_show_salut_dialog (EmpathyAccountsDialog *self)
2185 {
2186   EmpathyAccountsDialogPriv *priv = GET_PRIV (self);
2187   GtkWidget *dialog, *widget, *content, *button;
2188   gint response;
2189
2190   if (!empathy_local_xmpp_assistant_widget_should_create_account (
2191         priv->account_manager))
2192     return;
2193
2194   widget = empathy_local_xmpp_assistant_widget_new ();
2195   gtk_widget_show (widget);
2196
2197   dialog = gtk_dialog_new ();
2198
2199   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
2200
2201   gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Skip"),
2202       GTK_RESPONSE_NO);
2203
2204   button = gtk_dialog_add_button (GTK_DIALOG (dialog),
2205       _("_Connect"), GTK_RESPONSE_YES);
2206   gtk_widget_set_sensitive (button, FALSE);
2207
2208   g_signal_connect (widget, "valid", G_CALLBACK (salut_valid_cb),
2209       button);
2210
2211   content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
2212   gtk_box_pack_start (GTK_BOX (content), widget, TRUE, TRUE, 0);
2213
2214   response = gtk_dialog_run (GTK_DIALOG (dialog));
2215
2216   if (response == GTK_RESPONSE_YES)
2217     {
2218       empathy_local_xmpp_assistant_widget_create_account (
2219           EMPATHY_LOCAL_XMPP_ASSISTANT_WIDGET (widget));
2220     }
2221
2222   gtk_widget_destroy (dialog);
2223 }
2224
2225 static void
2226 import_dialog_response_cb (GtkDialog *dialog,
2227     gint response_id,
2228     EmpathyAccountsDialog *self)
2229 {
2230   maybe_show_salut_dialog (self);
2231 }
2232
2233 static void
2234 maybe_show_import_dialog (EmpathyAccountsDialog *self)
2235 {
2236   EmpathyAccountsDialogPriv *priv = GET_PRIV (self);
2237   GtkWidget *dialog;
2238
2239   if (empathy_accounts_has_non_salut_accounts (priv->account_manager))
2240     return;
2241
2242   if (!empathy_import_accounts_to_import ())
2243     {
2244       maybe_show_salut_dialog (self);
2245       return;
2246     }
2247
2248   dialog = display_import_dialog (self);
2249
2250   tp_g_signal_connect_object (dialog, "response",
2251       G_CALLBACK (import_dialog_response_cb), self, 0);
2252 }
2253
2254 static void
2255 finished_loading (EmpathyAccountsDialog *self)
2256 {
2257   EmpathyAccountsDialogPriv *priv = GET_PRIV (self);
2258   GtkTreeSelection *selection;
2259   gboolean has_selected;
2260
2261   priv->loading = FALSE;
2262
2263   gtk_widget_set_sensitive (priv->button_add, TRUE);
2264   gtk_widget_set_sensitive (priv->button_import, TRUE);
2265   gtk_widget_set_sensitive (priv->treeview, TRUE);
2266
2267   /* Sensitive the remove button if there is an account selected */
2268   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
2269   has_selected = gtk_tree_selection_get_selected (selection, NULL, NULL);
2270   gtk_widget_set_sensitive (priv->button_remove, has_selected);
2271
2272   gtk_spinner_stop (GTK_SPINNER (priv->spinner));
2273   gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook_account),
2274       NOTEBOOK_PAGE_ACCOUNT);
2275
2276   maybe_show_import_dialog (self);
2277 }
2278
2279 static void
2280 accounts_dialog_cms_prepare_cb (GObject *source,
2281     GAsyncResult *result,
2282     gpointer user_data)
2283 {
2284   EmpathyConnectionManagers *cms = EMPATHY_CONNECTION_MANAGERS (source);
2285   EmpathyAccountsDialog *dialog = user_data;
2286   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2287
2288   if (!empathy_connection_managers_prepare_finish (cms, result, NULL))
2289     goto out;
2290
2291   /* No need to update the settings if we are already preparing one */
2292   if (priv->settings_ready == NULL)
2293     accounts_dialog_update_settings (dialog, NULL);
2294
2295   if (priv->initial_selection != NULL)
2296     {
2297       accounts_dialog_set_selected_account (dialog, priv->initial_selection);
2298       g_object_unref (priv->initial_selection);
2299       priv->initial_selection = NULL;
2300     }
2301
2302 out:
2303   finished_loading (dialog);
2304 }
2305
2306 static void
2307 accounts_dialog_accounts_setup (EmpathyAccountsDialog *dialog)
2308 {
2309   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2310   GList *accounts, *l;
2311
2312   g_signal_connect (priv->account_manager, "account-validity-changed",
2313       G_CALLBACK (accounts_dialog_account_validity_changed_cb),
2314       dialog);
2315   g_signal_connect (priv->account_manager, "account-removed",
2316       G_CALLBACK (accounts_dialog_account_removed_cb),
2317       dialog);
2318   g_signal_connect (priv->account_manager, "account-enabled",
2319       G_CALLBACK (accounts_dialog_account_enabled_cb),
2320       dialog);
2321   g_signal_connect (priv->account_manager, "account-disabled",
2322       G_CALLBACK (accounts_dialog_account_disabled_cb),
2323       dialog);
2324
2325   /* Add existing accounts */
2326   accounts = tp_account_manager_get_valid_accounts (priv->account_manager);
2327   for (l = accounts; l; l = l->next)
2328     {
2329       accounts_dialog_add_account (dialog, l->data);
2330     }
2331   g_list_free (accounts);
2332
2333   priv->cms = empathy_connection_managers_dup_singleton ();
2334
2335   empathy_connection_managers_prepare_async (priv->cms,
2336       accounts_dialog_cms_prepare_cb, dialog);
2337
2338   accounts_dialog_model_select_first (dialog);
2339 }
2340
2341 static void
2342 accounts_dialog_manager_ready_cb (GObject *source_object,
2343     GAsyncResult *result,
2344     gpointer user_data)
2345 {
2346   TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object);
2347   GError *error = NULL;
2348
2349   if (!tp_proxy_prepare_finish (manager, result, &error))
2350     {
2351       DEBUG ("Failed to prepare account manager: %s", error->message);
2352       g_error_free (error);
2353       return;
2354     }
2355
2356   accounts_dialog_accounts_setup (user_data);
2357 }
2358
2359 static void
2360 dialog_response_cb (GtkWidget *widget,
2361     gint response_id,
2362     gpointer user_data)
2363 {
2364   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (widget);
2365
2366   if (response_id == GTK_RESPONSE_HELP)
2367     {
2368       empathy_url_show (widget, "help:empathy/accounts-window");
2369     }
2370   else if (response_id == GTK_RESPONSE_CLOSE ||
2371       response_id == GTK_RESPONSE_DELETE_EVENT)
2372     {
2373       TpAccount *account = NULL;
2374
2375       if (accounts_dialog_has_pending_change (dialog, &account))
2376         {
2377           gchar *question_dialog_primary_text = get_dialog_primary_text (
2378               account);
2379
2380           accounts_dialog_show_question_dialog (dialog,
2381               question_dialog_primary_text,
2382               _("You are about to close the window, which will discard\n"
2383                   "your changes. Are you sure you want to proceed?"),
2384               G_CALLBACK (accounts_dialog_close_response_cb),
2385               widget,
2386               GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
2387               GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
2388
2389           g_free (question_dialog_primary_text);
2390         }
2391       else
2392         {
2393           gtk_widget_destroy (widget);
2394         }
2395
2396       if (account != NULL)
2397         g_object_unref (account);
2398     }
2399 }
2400
2401 static void
2402 accounts_dialog_build_ui (EmpathyAccountsDialog *dialog)
2403 {
2404   GtkWidget *top_hbox;
2405   GtkBuilder                   *gui;
2406   gchar                        *filename;
2407   EmpathyAccountsDialogPriv    *priv = GET_PRIV (dialog);
2408   GtkWidget *content_area, *action_area;
2409   GtkWidget *grid, *hbox;
2410   GtkWidget *alig;
2411   GtkWidget *sw, *toolbar;
2412   GtkStyleContext *context;
2413
2414   filename = empathy_file_lookup ("empathy-accounts-dialog.ui", "src");
2415
2416   gui = empathy_builder_get_file (filename,
2417       "accounts_dialog_hbox", &top_hbox,
2418       "vbox_details", &priv->vbox_details,
2419       "alignment_settings", &priv->alignment_settings,
2420       "alignment_infobar", &priv->alignment_infobar,
2421       "treeview", &priv->treeview,
2422       "button_add", &priv->button_add,
2423       "button_remove", &priv->button_remove,
2424       "button_import", &priv->button_import,
2425       "notebook_account", &priv->notebook_account,
2426       "alignment_loading", &alig,
2427       "accounts_sw", &sw,
2428       "add_remove_toolbar", &toolbar,
2429       NULL);
2430   g_free (filename);
2431
2432   empathy_builder_connect (gui, dialog,
2433       "button_add", "clicked", accounts_dialog_button_add_clicked_cb,
2434       "button_remove", "clicked", accounts_dialog_button_remove_clicked_cb,
2435       "button_import", "clicked", accounts_dialog_button_import_clicked_cb,
2436       "treeview", "button-press-event",
2437          accounts_dialog_treeview_button_press_event_cb,
2438       NULL);
2439
2440   content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
2441
2442   gtk_box_pack_start (GTK_BOX (content_area), top_hbox, TRUE, TRUE, 0);
2443
2444   g_object_unref (gui);
2445
2446   action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
2447
2448 #ifdef HAVE_MEEGO
2449   gtk_widget_hide (action_area);
2450   gtk_widget_hide (priv->button_remove);
2451 #endif /* HAVE_MEEGO */
2452
2453   /* Display loading page */
2454   priv->loading = TRUE;
2455
2456   priv->spinner = gtk_spinner_new ();
2457
2458   gtk_spinner_start (GTK_SPINNER (priv->spinner));
2459   gtk_widget_show (priv->spinner);
2460
2461   gtk_container_add (GTK_CONTAINER (alig), priv->spinner);
2462
2463   gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook_account),
2464       NOTEBOOK_PAGE_LOADING);
2465
2466   /* Remove button is insensitive until we have a selected account */
2467   gtk_widget_set_sensitive (priv->button_remove, FALSE);
2468
2469   /* Add and Import buttons and treeview are insensitive while the dialog
2470    * is loading */
2471   gtk_widget_set_sensitive (priv->button_add, FALSE);
2472   gtk_widget_set_sensitive (priv->button_import, FALSE);
2473   gtk_widget_set_sensitive (priv->treeview, FALSE);
2474
2475   if (priv->parent_window)
2476     gtk_window_set_transient_for (GTK_WINDOW (dialog),
2477         priv->parent_window);
2478
2479   priv->infobar = gtk_info_bar_new ();
2480   gtk_container_add (GTK_CONTAINER (priv->alignment_infobar),
2481       priv->infobar);
2482   gtk_widget_show (priv->infobar);
2483
2484   grid = gtk_grid_new ();
2485   gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
2486   gtk_container_add (
2487       GTK_CONTAINER (gtk_info_bar_get_content_area (
2488           GTK_INFO_BAR (priv->infobar))),
2489       grid);
2490
2491   priv->image_type = gtk_image_new_from_stock (GTK_STOCK_CUT,
2492       GTK_ICON_SIZE_DIALOG);
2493   gtk_misc_set_alignment (GTK_MISC (priv->image_type), 0.0, 0.5);
2494   gtk_grid_attach (GTK_GRID (grid), priv->image_type, 0, 0, 1, 2);
2495
2496   /* first row */
2497   priv->label_name = gtk_label_new (NULL);
2498   gtk_grid_attach (GTK_GRID (grid), priv->label_name, 1, 0, 1, 1);
2499
2500   /* second row */
2501   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
2502   gtk_widget_set_hexpand (hbox, TRUE);
2503   gtk_widget_set_halign (hbox, GTK_ALIGN_CENTER);
2504   gtk_grid_attach (GTK_GRID (grid), hbox, 1, 1, 1, 1);
2505
2506   /* set up spinner */
2507   priv->throbber = gtk_spinner_new ();
2508
2509   priv->image_status = gtk_image_new_from_icon_name (
2510             empathy_icon_name_for_presence (
2511             TP_CONNECTION_PRESENCE_TYPE_OFFLINE), GTK_ICON_SIZE_SMALL_TOOLBAR);
2512
2513   priv->label_status = gtk_label_new (NULL);
2514   gtk_label_set_ellipsize (GTK_LABEL (priv->label_status), PANGO_ELLIPSIZE_END);
2515
2516   gtk_box_pack_start (GTK_BOX (hbox), priv->throbber, FALSE, FALSE, 0);
2517   gtk_box_pack_start (GTK_BOX (hbox), priv->image_status, FALSE, FALSE, 0);
2518   gtk_box_pack_start (GTK_BOX (hbox), priv->label_status, FALSE, FALSE, 0);
2519
2520   /* enabled switch */
2521   priv->enabled_switch = gtk_switch_new ();
2522   gtk_widget_set_valign (priv->enabled_switch, GTK_ALIGN_CENTER);
2523   g_signal_connect (priv->enabled_switch, "notify::active",
2524       G_CALLBACK (accounts_dialog_enable_switch_active_cb), dialog);
2525   gtk_grid_attach (GTK_GRID (grid), priv->enabled_switch, 2, 0, 1, 2);
2526
2527   gtk_widget_show_all (grid);
2528
2529   /* Tweak the dialog */
2530   gtk_window_set_title (GTK_WINDOW (dialog), _("Messaging and VoIP Accounts"));
2531   gtk_window_set_role (GTK_WINDOW (dialog), "accounts");
2532
2533   gtk_window_set_default_size (GTK_WINDOW (dialog), 640, 450);
2534
2535   gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
2536
2537   /* join the add/remove toolbar to the treeview */
2538   context = gtk_widget_get_style_context (sw);
2539   gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
2540
2541   context = gtk_widget_get_style_context (toolbar);
2542   gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
2543
2544   /* add dialog buttons */
2545   gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), GTK_BUTTONBOX_END);
2546
2547   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
2548       GTK_STOCK_HELP, GTK_RESPONSE_HELP,
2549       GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
2550       NULL);
2551
2552   g_signal_connect (dialog, "response",
2553       G_CALLBACK (dialog_response_cb), dialog);
2554
2555   g_signal_connect (dialog, "delete-event",
2556       G_CALLBACK (accounts_dialog_delete_event_cb), dialog);
2557 }
2558
2559 static void
2560 do_dispose (GObject *obj)
2561 {
2562   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (obj);
2563   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2564
2565   if (priv->connecting_id != 0)
2566     {
2567       g_source_remove (priv->connecting_id);
2568       priv->connecting_id = 0;
2569     }
2570
2571   if (priv->account_manager != NULL)
2572     {
2573       g_object_unref (priv->account_manager);
2574       priv->account_manager = NULL;
2575     }
2576
2577   if (priv->cms != NULL)
2578     {
2579       g_object_unref (priv->cms);
2580       priv->cms = NULL;
2581     }
2582
2583   if (priv->connectivity)
2584     {
2585       g_object_unref (priv->connectivity);
2586       priv->connectivity = NULL;
2587     }
2588
2589   if (priv->initial_selection != NULL)
2590     {
2591       g_object_unref (priv->initial_selection);
2592       priv->initial_selection = NULL;
2593     }
2594
2595   G_OBJECT_CLASS (empathy_accounts_dialog_parent_class)->dispose (obj);
2596 }
2597
2598 static void
2599 do_get_property (GObject *object,
2600     guint property_id,
2601     GValue *value,
2602     GParamSpec *pspec)
2603 {
2604   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
2605
2606   switch (property_id)
2607     {
2608     case PROP_PARENT:
2609       g_value_set_object (value, priv->parent_window);
2610       break;
2611     default:
2612       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2613     }
2614 }
2615
2616 static void
2617 do_set_property (GObject *object,
2618     guint property_id,
2619     const GValue *value,
2620     GParamSpec *pspec)
2621 {
2622   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
2623
2624   switch (property_id)
2625     {
2626     case PROP_PARENT:
2627       priv->parent_window = g_value_get_object (value);
2628       break;
2629     default:
2630       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2631     }
2632 }
2633
2634 static void
2635 do_constructed (GObject *object)
2636 {
2637   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (object);
2638   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2639   GtkTreeModel *model;
2640
2641   accounts_dialog_build_ui (dialog);
2642   accounts_dialog_model_setup (dialog);
2643
2644   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
2645   g_signal_connect (model, "row-inserted",
2646       (GCallback) accounts_dialog_accounts_model_row_inserted_cb, dialog);
2647   g_signal_connect (model, "row-deleted",
2648       (GCallback) accounts_dialog_accounts_model_row_deleted_cb, dialog);
2649
2650   /* Set up signalling */
2651   priv->account_manager = tp_account_manager_dup ();
2652
2653   tp_proxy_prepare_async (priv->account_manager, NULL,
2654       accounts_dialog_manager_ready_cb, dialog);
2655
2656   priv->connectivity = empathy_connectivity_dup_singleton ();
2657 }
2658
2659 static void
2660 empathy_accounts_dialog_class_init (EmpathyAccountsDialogClass *klass)
2661 {
2662   GObjectClass *oclass = G_OBJECT_CLASS (klass);
2663   GParamSpec *param_spec;
2664
2665   oclass->dispose = do_dispose;
2666   oclass->constructed = do_constructed;
2667   oclass->set_property = do_set_property;
2668   oclass->get_property = do_get_property;
2669
2670   param_spec = g_param_spec_object ("parent",
2671       "parent", "The parent window",
2672       GTK_TYPE_WINDOW,
2673       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2674   g_object_class_install_property (oclass, PROP_PARENT, param_spec);
2675
2676   g_type_class_add_private (klass, sizeof (EmpathyAccountsDialogPriv));
2677 }
2678
2679 static void
2680 empathy_accounts_dialog_init (EmpathyAccountsDialog *dialog)
2681 {
2682   EmpathyAccountsDialogPriv *priv;
2683
2684   priv = G_TYPE_INSTANCE_GET_PRIVATE ((dialog),
2685       EMPATHY_TYPE_ACCOUNTS_DIALOG,
2686       EmpathyAccountsDialogPriv);
2687   dialog->priv = priv;
2688 }
2689
2690 /* public methods */
2691
2692 GtkWidget *
2693 empathy_accounts_dialog_show (GtkWindow *parent,
2694     TpAccount *selected_account)
2695 {
2696   EmpathyAccountsDialog *dialog;
2697   EmpathyAccountsDialogPriv *priv;
2698
2699   dialog = g_object_new (EMPATHY_TYPE_ACCOUNTS_DIALOG,
2700       "parent", parent, NULL);
2701
2702   priv = GET_PRIV (dialog);
2703
2704   if (selected_account)
2705     {
2706       if (priv->cms != NULL && empathy_connection_managers_is_ready (priv->cms))
2707         accounts_dialog_set_selected_account (dialog, selected_account);
2708       else
2709         /* save the selection to set it later when the cms
2710          * becomes ready.
2711          */
2712         priv->initial_selection = g_object_ref (selected_account);
2713     }
2714
2715   gtk_window_present (GTK_WINDOW (dialog));
2716
2717   return GTK_WIDGET (dialog);
2718 }
2719
2720 void
2721 empathy_accounts_dialog_show_application (GdkScreen *screen,
2722     TpAccount *selected_account,
2723     gboolean if_needed,
2724     gboolean hidden)
2725 {
2726   GString *args;
2727
2728   g_return_if_fail (!selected_account || TP_IS_ACCOUNT (selected_account));
2729
2730   args = g_string_new (NULL);
2731
2732   if (selected_account != NULL)
2733     g_string_append_printf (args, " --select-account=%s",
2734         tp_account_get_path_suffix (selected_account));
2735
2736   if (if_needed)
2737     g_string_append_printf (args, " --if-needed");
2738
2739   if (hidden)
2740     g_string_append_printf (args, " --hidden");
2741
2742   DEBUG ("Launching empathy-accounts (if_needed: %d, hidden: %d, account: %s)",
2743     if_needed, hidden,
2744     selected_account == NULL ? "<none selected>" :
2745       tp_proxy_get_object_path (TP_PROXY (selected_account)));
2746
2747   empathy_launch_program (BIN_DIR, "empathy-accounts", args->str);
2748
2749   g_string_free (args, TRUE);
2750 }
2751
2752 gboolean
2753 empathy_accounts_dialog_is_creating (EmpathyAccountsDialog *dialog)
2754 {
2755   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2756   gboolean result = FALSE;
2757
2758   if (priv->setting_widget_object == NULL)
2759     goto out;
2760
2761   g_object_get (priv->setting_widget_object,
2762       "creating-account", &result, NULL);
2763
2764 out:
2765   return result;
2766 }