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