]> git.0d.be Git - empathy.git/blob - src/empathy-accounts-dialog.c
c79ac407808f50f4d687268828e4fa0d41d04e5b
[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 accounts_dialog_account_validity_changed_cb (TpAccountManager *manager,
1984     TpAccount *account,
1985     gboolean valid,
1986     EmpathyAccountsDialog *dialog)
1987 {
1988   accounts_dialog_add_account (dialog, account);
1989 }
1990
1991 static void
1992 accounts_dialog_accounts_model_row_inserted_cb (GtkTreeModel *model,
1993     GtkTreePath *path,
1994     GtkTreeIter *iter,
1995     EmpathyAccountsDialog *dialog)
1996 {
1997   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1998
1999   if (priv->setting_widget_object != NULL &&
2000       accounts_dialog_has_valid_accounts (dialog))
2001     {
2002       empathy_account_widget_set_other_accounts_exist (
2003           priv->setting_widget_object, TRUE);
2004     }
2005 }
2006
2007 static void
2008 accounts_dialog_accounts_model_row_deleted_cb (GtkTreeModel *model,
2009     GtkTreePath *path,
2010     EmpathyAccountsDialog *dialog)
2011 {
2012   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2013
2014   if (priv->setting_widget_object != NULL &&
2015       !accounts_dialog_has_valid_accounts (dialog))
2016     {
2017       empathy_account_widget_set_other_accounts_exist (
2018           priv->setting_widget_object, FALSE);
2019     }
2020 }
2021
2022 static void
2023 accounts_dialog_account_removed_cb (TpAccountManager *manager,
2024     TpAccount *account,
2025     EmpathyAccountsDialog *dialog)
2026 {
2027   GtkTreeIter iter;
2028   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2029
2030   if (accounts_dialog_get_account_iter (dialog, account, &iter))
2031     {
2032       gtk_list_store_remove (GTK_LIST_STORE (
2033           gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview))), &iter);
2034     }
2035 }
2036
2037 static void
2038 enable_or_disable_account (EmpathyAccountsDialog *dialog,
2039     TpAccount *account,
2040     gboolean enabled)
2041 {
2042   /* Update the status-infobar in the details view */
2043   accounts_dialog_update_status_infobar (dialog, account);
2044
2045   DEBUG ("Account %s is now %s",
2046       tp_account_get_display_name (account),
2047       enabled ? "enabled" : "disabled");
2048 }
2049
2050 static void
2051 accounts_dialog_account_disabled_cb (TpAccountManager *manager,
2052     TpAccount *account,
2053     EmpathyAccountsDialog *dialog)
2054 {
2055   enable_or_disable_account (dialog, account, FALSE);
2056   update_account_in_treeview (dialog, account);
2057 }
2058
2059 static void
2060 accounts_dialog_account_enabled_cb (TpAccountManager *manager,
2061     TpAccount *account,
2062     EmpathyAccountsDialog *dialog)
2063 {
2064   enable_or_disable_account (dialog, account, TRUE);
2065 }
2066
2067 static void
2068 accounts_dialog_button_import_clicked_cb (GtkWidget *button,
2069     EmpathyAccountsDialog *dialog)
2070 {
2071   GtkWidget *import_dialog;
2072
2073   import_dialog = empathy_import_dialog_new (GTK_WINDOW (dialog),
2074       FALSE);
2075   gtk_widget_show (import_dialog);
2076 }
2077
2078 static void
2079 accounts_dialog_close_response_cb (GtkDialog *message_dialog,
2080   gint response_id,
2081   gpointer user_data)
2082 {
2083   GtkWidget *account_dialog = GTK_WIDGET (user_data);
2084
2085   gtk_widget_destroy (GTK_WIDGET (message_dialog));
2086
2087   if (response_id == GTK_RESPONSE_YES)
2088     gtk_widget_destroy (account_dialog);
2089 }
2090
2091 static gboolean
2092 accounts_dialog_delete_event_cb (GtkWidget *widget,
2093     GdkEvent *event,
2094     EmpathyAccountsDialog *dialog)
2095 {
2096   /* we maunally handle responses to delete events */
2097   return TRUE;
2098 }
2099
2100 static void
2101 accounts_dialog_set_selected_account (EmpathyAccountsDialog *dialog,
2102     TpAccount *account)
2103 {
2104   GtkTreeIter       iter;
2105
2106   if (accounts_dialog_get_account_iter (dialog, account, &iter))
2107     select_and_scroll_to_iter (dialog, &iter);
2108 }
2109
2110 static void
2111 finished_loading (EmpathyAccountsDialog *self)
2112 {
2113   EmpathyAccountsDialogPriv *priv = GET_PRIV (self);
2114   GtkTreeSelection *selection;
2115   gboolean has_selected;
2116
2117   priv->loading = FALSE;
2118
2119   gtk_widget_set_sensitive (priv->button_add, TRUE);
2120   gtk_widget_set_sensitive (priv->button_import, TRUE);
2121   gtk_widget_set_sensitive (priv->treeview, TRUE);
2122
2123   /* Sensitive the remove button if there is an account selected */
2124   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
2125   has_selected = gtk_tree_selection_get_selected (selection, NULL, NULL);
2126   gtk_widget_set_sensitive (priv->button_remove, has_selected);
2127
2128   gtk_spinner_stop (GTK_SPINNER (priv->spinner));
2129   gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook_account),
2130       NOTEBOOK_PAGE_ACCOUNT);
2131 }
2132
2133 static void
2134 accounts_dialog_cms_prepare_cb (GObject *source,
2135     GAsyncResult *result,
2136     gpointer user_data)
2137 {
2138   EmpathyConnectionManagers *cms = EMPATHY_CONNECTION_MANAGERS (source);
2139   EmpathyAccountsDialog *dialog = user_data;
2140   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2141
2142   if (!empathy_connection_managers_prepare_finish (cms, result, NULL))
2143     goto out;
2144
2145   /* No need to update the settings if we are already preparing one */
2146   if (priv->settings_ready == NULL)
2147     accounts_dialog_update_settings (dialog, NULL);
2148
2149   if (priv->initial_selection != NULL)
2150     {
2151       accounts_dialog_set_selected_account (dialog, priv->initial_selection);
2152       g_object_unref (priv->initial_selection);
2153       priv->initial_selection = NULL;
2154     }
2155
2156 out:
2157   finished_loading (dialog);
2158 }
2159
2160 static void
2161 accounts_dialog_accounts_setup (EmpathyAccountsDialog *dialog)
2162 {
2163   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2164   GList *accounts, *l;
2165
2166   g_signal_connect (priv->account_manager, "account-validity-changed",
2167       G_CALLBACK (accounts_dialog_account_validity_changed_cb),
2168       dialog);
2169   g_signal_connect (priv->account_manager, "account-removed",
2170       G_CALLBACK (accounts_dialog_account_removed_cb),
2171       dialog);
2172   g_signal_connect (priv->account_manager, "account-enabled",
2173       G_CALLBACK (accounts_dialog_account_enabled_cb),
2174       dialog);
2175   g_signal_connect (priv->account_manager, "account-disabled",
2176       G_CALLBACK (accounts_dialog_account_disabled_cb),
2177       dialog);
2178
2179   /* Add existing accounts */
2180   accounts = tp_account_manager_get_valid_accounts (priv->account_manager);
2181   for (l = accounts; l; l = l->next)
2182     {
2183       accounts_dialog_add_account (dialog, l->data);
2184     }
2185   g_list_free (accounts);
2186
2187   priv->cms = empathy_connection_managers_dup_singleton ();
2188
2189   empathy_connection_managers_prepare_async (priv->cms,
2190       accounts_dialog_cms_prepare_cb, dialog);
2191
2192   accounts_dialog_model_select_first (dialog);
2193 }
2194
2195 static void
2196 accounts_dialog_manager_ready_cb (GObject *source_object,
2197     GAsyncResult *result,
2198     gpointer user_data)
2199 {
2200   TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object);
2201   GError *error = NULL;
2202
2203   if (!tp_proxy_prepare_finish (manager, result, &error))
2204     {
2205       DEBUG ("Failed to prepare account manager: %s", error->message);
2206       g_error_free (error);
2207       return;
2208     }
2209
2210   accounts_dialog_accounts_setup (user_data);
2211 }
2212
2213 static void
2214 dialog_response_cb (GtkWidget *widget,
2215     gint response_id,
2216     gpointer user_data)
2217 {
2218   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (widget);
2219
2220   if (response_id == GTK_RESPONSE_HELP)
2221     {
2222       empathy_url_show (widget, "ghelp:empathy?accounts-window");
2223     }
2224   else if (response_id == GTK_RESPONSE_CLOSE ||
2225       response_id == GTK_RESPONSE_DELETE_EVENT)
2226     {
2227       TpAccount *account = NULL;
2228
2229       if (accounts_dialog_has_pending_change (dialog, &account))
2230         {
2231           gchar *question_dialog_primary_text = get_dialog_primary_text (
2232               account);
2233
2234           accounts_dialog_show_question_dialog (dialog,
2235               question_dialog_primary_text,
2236               _("You are about to close the window, which will discard\n"
2237                   "your changes. Are you sure you want to proceed?"),
2238               G_CALLBACK (accounts_dialog_close_response_cb),
2239               widget,
2240               GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
2241               GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
2242
2243           g_free (question_dialog_primary_text);
2244         }
2245       else
2246         {
2247           gtk_widget_destroy (widget);
2248         }
2249
2250       if (account != NULL)
2251         g_object_unref (account);
2252     }
2253 }
2254
2255 static void
2256 accounts_dialog_build_ui (EmpathyAccountsDialog *dialog)
2257 {
2258   GtkWidget *top_hbox;
2259   GtkBuilder                   *gui;
2260   gchar                        *filename;
2261   EmpathyAccountsDialogPriv    *priv = GET_PRIV (dialog);
2262   GtkWidget *content_area, *action_area;
2263   GtkWidget *grid, *hbox;
2264   GtkWidget *alig;
2265   GtkWidget *sw, *toolbar;
2266   GtkStyleContext *context;
2267
2268   filename = empathy_file_lookup ("empathy-accounts-dialog.ui", "src");
2269
2270   gui = empathy_builder_get_file (filename,
2271       "accounts_dialog_hbox", &top_hbox,
2272       "vbox_details", &priv->vbox_details,
2273       "frame_no_protocol", &priv->frame_no_protocol,
2274       "alignment_settings", &priv->alignment_settings,
2275       "alignment_infobar", &priv->alignment_infobar,
2276       "treeview", &priv->treeview,
2277       "button_add", &priv->button_add,
2278       "button_remove", &priv->button_remove,
2279       "button_import", &priv->button_import,
2280       "hbox_protocol", &priv->hbox_protocol,
2281       "notebook_account", &priv->notebook_account,
2282       "alignment_loading", &alig,
2283       "accounts_sw", &sw,
2284       "add_remove_toolbar", &toolbar,
2285       NULL);
2286   g_free (filename);
2287
2288   gtk_widget_set_no_show_all (priv->frame_no_protocol, TRUE);
2289
2290   empathy_builder_connect (gui, dialog,
2291       "button_add", "clicked", accounts_dialog_button_add_clicked_cb,
2292       "button_remove", "clicked", accounts_dialog_button_remove_clicked_cb,
2293       "button_import", "clicked", accounts_dialog_button_import_clicked_cb,
2294       "treeview", "button-press-event",
2295          accounts_dialog_treeview_button_press_event_cb,
2296       NULL);
2297
2298   content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
2299
2300   gtk_box_pack_start (GTK_BOX (content_area), top_hbox, TRUE, TRUE, 0);
2301
2302   g_object_unref (gui);
2303
2304   action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
2305
2306 #ifdef HAVE_MEEGO
2307   gtk_widget_hide (action_area);
2308   gtk_widget_hide (priv->button_remove);
2309 #endif /* HAVE_MEEGO */
2310
2311   /* Display loading page */
2312   priv->loading = TRUE;
2313
2314   priv->spinner = gtk_spinner_new ();
2315
2316   gtk_spinner_start (GTK_SPINNER (priv->spinner));
2317   gtk_widget_show (priv->spinner);
2318
2319   gtk_container_add (GTK_CONTAINER (alig), priv->spinner);
2320
2321   gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook_account),
2322       NOTEBOOK_PAGE_LOADING);
2323
2324   /* Remove button is insensitive until we have a selected account */
2325   gtk_widget_set_sensitive (priv->button_remove, FALSE);
2326
2327   /* Add and Import buttons and treeview are insensitive while the dialog
2328    * is loading */
2329   gtk_widget_set_sensitive (priv->button_add, FALSE);
2330   gtk_widget_set_sensitive (priv->button_import, FALSE);
2331   gtk_widget_set_sensitive (priv->treeview, FALSE);
2332
2333   if (priv->parent_window)
2334     gtk_window_set_transient_for (GTK_WINDOW (dialog),
2335         priv->parent_window);
2336
2337   priv->infobar = gtk_info_bar_new ();
2338   gtk_container_add (GTK_CONTAINER (priv->alignment_infobar),
2339       priv->infobar);
2340   gtk_widget_show (priv->infobar);
2341
2342   grid = gtk_grid_new ();
2343   gtk_container_add (
2344       GTK_CONTAINER (gtk_info_bar_get_content_area (
2345           GTK_INFO_BAR (priv->infobar))),
2346       grid);
2347
2348   priv->image_type = gtk_image_new_from_stock (GTK_STOCK_CUT,
2349       GTK_ICON_SIZE_DIALOG);
2350   gtk_misc_set_alignment (GTK_MISC (priv->image_type), 0.0, 0.5);
2351   gtk_grid_attach (GTK_GRID (grid), priv->image_type, 0, 0, 1, 2);
2352
2353   /* first row */
2354   priv->label_name = gtk_label_new (NULL);
2355   gtk_grid_attach (GTK_GRID (grid), priv->label_name, 1, 0, 1, 1);
2356
2357   /* second row */
2358   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
2359   gtk_widget_set_hexpand (hbox, TRUE);
2360   gtk_widget_set_halign (hbox, GTK_ALIGN_CENTER);
2361   gtk_grid_attach (GTK_GRID (grid), hbox, 1, 1, 1, 1);
2362
2363   /* set up spinner */
2364   priv->throbber = gtk_spinner_new ();
2365
2366   priv->image_status = gtk_image_new_from_icon_name (
2367             empathy_icon_name_for_presence (
2368             TP_CONNECTION_PRESENCE_TYPE_OFFLINE), GTK_ICON_SIZE_SMALL_TOOLBAR);
2369
2370   priv->label_status = gtk_label_new (NULL);
2371   gtk_label_set_line_wrap (GTK_LABEL (priv->label_status), TRUE);
2372
2373   gtk_box_pack_start (GTK_BOX (hbox), priv->throbber, FALSE, FALSE, 0);
2374   gtk_box_pack_start (GTK_BOX (hbox), priv->image_status, FALSE, FALSE, 0);
2375   gtk_box_pack_start (GTK_BOX (hbox), priv->label_status, FALSE, FALSE, 0);
2376
2377   /* enabled switch */
2378   priv->enabled_switch = gtk_switch_new ();
2379   gtk_widget_set_valign (priv->enabled_switch, GTK_ALIGN_CENTER);
2380   g_signal_connect (priv->enabled_switch, "notify::active",
2381       G_CALLBACK (accounts_dialog_enable_switch_active_cb), dialog);
2382   gtk_grid_attach (GTK_GRID (grid), priv->enabled_switch, 2, 0, 1, 2);
2383
2384   gtk_widget_show_all (grid);
2385
2386   /* Tweak the dialog */
2387   gtk_window_set_title (GTK_WINDOW (dialog), _("Messaging and VoIP Accounts"));
2388   gtk_window_set_role (GTK_WINDOW (dialog), "accounts");
2389
2390   gtk_window_set_default_size (GTK_WINDOW (dialog), 640, 450);
2391
2392   gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
2393
2394   /* join the add/remove toolbar to the treeview */
2395   context = gtk_widget_get_style_context (sw);
2396   gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
2397
2398   context = gtk_widget_get_style_context (toolbar);
2399   gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
2400
2401   /* add dialog buttons */
2402   gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), GTK_BUTTONBOX_END);
2403
2404   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
2405       GTK_STOCK_HELP, GTK_RESPONSE_HELP,
2406       GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
2407       NULL);
2408
2409   g_signal_connect (dialog, "response",
2410       G_CALLBACK (dialog_response_cb), dialog);
2411
2412   g_signal_connect (dialog, "delete-event",
2413       G_CALLBACK (accounts_dialog_delete_event_cb), dialog);
2414 }
2415
2416 static void
2417 do_dispose (GObject *obj)
2418 {
2419   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (obj);
2420   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2421
2422   if (priv->connecting_id != 0)
2423     {
2424       g_source_remove (priv->connecting_id);
2425       priv->connecting_id = 0;
2426     }
2427
2428   if (priv->account_manager != NULL)
2429     {
2430       g_object_unref (priv->account_manager);
2431       priv->account_manager = NULL;
2432     }
2433
2434   if (priv->cms != NULL)
2435     {
2436       g_object_unref (priv->cms);
2437       priv->cms = NULL;
2438     }
2439
2440   if (priv->connectivity)
2441     {
2442       g_object_unref (priv->connectivity);
2443       priv->connectivity = NULL;
2444     }
2445
2446   if (priv->initial_selection != NULL)
2447     {
2448       g_object_unref (priv->initial_selection);
2449       priv->initial_selection = NULL;
2450     }
2451
2452   G_OBJECT_CLASS (empathy_accounts_dialog_parent_class)->dispose (obj);
2453 }
2454
2455 static void
2456 do_get_property (GObject *object,
2457     guint property_id,
2458     GValue *value,
2459     GParamSpec *pspec)
2460 {
2461   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
2462
2463   switch (property_id)
2464     {
2465     case PROP_PARENT:
2466       g_value_set_object (value, priv->parent_window);
2467       break;
2468     default:
2469       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2470     }
2471 }
2472
2473 static void
2474 do_set_property (GObject *object,
2475     guint property_id,
2476     const GValue *value,
2477     GParamSpec *pspec)
2478 {
2479   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
2480
2481   switch (property_id)
2482     {
2483     case PROP_PARENT:
2484       priv->parent_window = g_value_get_object (value);
2485       break;
2486     default:
2487       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2488     }
2489 }
2490
2491 static void
2492 do_constructed (GObject *object)
2493 {
2494   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (object);
2495   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2496   GtkTreeModel *model;
2497
2498   accounts_dialog_build_ui (dialog);
2499   accounts_dialog_model_setup (dialog);
2500
2501   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
2502   g_signal_connect (model, "row-inserted",
2503       (GCallback) accounts_dialog_accounts_model_row_inserted_cb, dialog);
2504   g_signal_connect (model, "row-deleted",
2505       (GCallback) accounts_dialog_accounts_model_row_deleted_cb, dialog);
2506
2507   /* Set up signalling */
2508   priv->account_manager = tp_account_manager_dup ();
2509
2510   tp_proxy_prepare_async (priv->account_manager, NULL,
2511       accounts_dialog_manager_ready_cb, dialog);
2512
2513   priv->connectivity = empathy_connectivity_dup_singleton ();
2514 }
2515
2516 static void
2517 empathy_accounts_dialog_class_init (EmpathyAccountsDialogClass *klass)
2518 {
2519   GObjectClass *oclass = G_OBJECT_CLASS (klass);
2520   GParamSpec *param_spec;
2521
2522   oclass->dispose = do_dispose;
2523   oclass->constructed = do_constructed;
2524   oclass->set_property = do_set_property;
2525   oclass->get_property = do_get_property;
2526
2527   param_spec = g_param_spec_object ("parent",
2528       "parent", "The parent window",
2529       GTK_TYPE_WINDOW,
2530       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2531   g_object_class_install_property (oclass, PROP_PARENT, param_spec);
2532
2533   g_type_class_add_private (klass, sizeof (EmpathyAccountsDialogPriv));
2534 }
2535
2536 static void
2537 empathy_accounts_dialog_init (EmpathyAccountsDialog *dialog)
2538 {
2539   EmpathyAccountsDialogPriv *priv;
2540
2541   priv = G_TYPE_INSTANCE_GET_PRIVATE ((dialog),
2542       EMPATHY_TYPE_ACCOUNTS_DIALOG,
2543       EmpathyAccountsDialogPriv);
2544   dialog->priv = priv;
2545 }
2546
2547 /* public methods */
2548
2549 GtkWidget *
2550 empathy_accounts_dialog_show (GtkWindow *parent,
2551     TpAccount *selected_account)
2552 {
2553   EmpathyAccountsDialog *dialog;
2554   EmpathyAccountsDialogPriv *priv;
2555
2556   dialog = g_object_new (EMPATHY_TYPE_ACCOUNTS_DIALOG,
2557       "parent", parent, NULL);
2558
2559   priv = GET_PRIV (dialog);
2560
2561   if (selected_account)
2562     {
2563       if (priv->cms != NULL && empathy_connection_managers_is_ready (priv->cms))
2564         accounts_dialog_set_selected_account (dialog, selected_account);
2565       else
2566         /* save the selection to set it later when the cms
2567          * becomes ready.
2568          */
2569         priv->initial_selection = g_object_ref (selected_account);
2570     }
2571
2572   gtk_window_present (GTK_WINDOW (dialog));
2573
2574   return GTK_WIDGET (dialog);
2575 }
2576
2577 void
2578 empathy_accounts_dialog_show_application (GdkScreen *screen,
2579     TpAccount *selected_account,
2580     gboolean if_needed,
2581     gboolean hidden)
2582 {
2583   GString *args;
2584
2585   g_return_if_fail (!selected_account || TP_IS_ACCOUNT (selected_account));
2586
2587   args = g_string_new (NULL);
2588
2589   if (selected_account != NULL)
2590     g_string_append_printf (args, " --select-account=%s",
2591         tp_account_get_path_suffix (selected_account));
2592
2593   if (if_needed)
2594     g_string_append_printf (args, " --if-needed");
2595
2596   if (hidden)
2597     g_string_append_printf (args, " --hidden");
2598
2599   DEBUG ("Launching empathy-accounts (if_needed: %d, hidden: %d, account: %s)",
2600     if_needed, hidden,
2601     selected_account == NULL ? "<none selected>" :
2602       tp_proxy_get_object_path (TP_PROXY (selected_account)));
2603
2604   empathy_launch_program (BIN_DIR, "empathy-accounts", args->str);
2605
2606   g_string_free (args, TRUE);
2607 }
2608
2609 gboolean
2610 empathy_accounts_dialog_is_creating (EmpathyAccountsDialog *dialog)
2611 {
2612   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2613   gboolean result = FALSE;
2614
2615   if (priv->setting_widget_object == NULL)
2616     goto out;
2617
2618   g_object_get (priv->setting_widget_object,
2619       "creating-account", &result, NULL);
2620
2621 out:
2622   return result;
2623 }