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