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