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