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