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