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