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