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