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