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