]> git.0d.be Git - empathy.git/blob - src/empathy-accounts-dialog.c
empathy-accounts-dialog: do_constructor: don't ref the dialog if it already exists...
[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.h>
33 #include <dbus/dbus-glib.h>
34
35 #include <telepathy-glib/util.h>
36
37 #include <libempathy/empathy-utils.h>
38 #include <libempathy/empathy-account-manager.h>
39 #include <libempathy/empathy-connection-managers.h>
40 #include <libempathy-gtk/empathy-ui-utils.h>
41
42 #include <libempathy-gtk/empathy-protocol-chooser.h>
43 #include <libempathy-gtk/empathy-account-widget.h>
44 #include <libempathy-gtk/empathy-account-widget-irc.h>
45 #include <libempathy-gtk/empathy-account-widget-sip.h>
46 #include <libempathy-gtk/empathy-cell-renderer-activatable.h>
47 #include <libempathy-gtk/empathy-conf.h>
48 #include <libempathy-gtk/empathy-images.h>
49
50 #include "empathy-accounts-dialog.h"
51 #include "empathy-import-dialog.h"
52 #include "empathy-import-utils.h"
53
54 #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
55 #include <libempathy/empathy-debug.h>
56
57 /* Flashing delay for icons (milliseconds). */
58 #define FLASH_TIMEOUT 500
59
60 /* The primary text of the dialog shown to the user when he is about to lose
61  * unsaved changes */
62 #define PENDING_CHANGES_QUESTION_PRIMARY_TEXT \
63   _("There are unsaved modification regarding your %s account.")
64
65 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccountsDialog)
66 G_DEFINE_TYPE (EmpathyAccountsDialog, empathy_accounts_dialog, G_TYPE_OBJECT);
67
68 static EmpathyAccountsDialog *dialog_singleton = NULL;
69
70 typedef struct {
71   GtkWidget *window;
72
73   GtkWidget *alignment_settings;
74
75   GtkWidget *vbox_details;
76   GtkWidget *frame_no_protocol;
77
78   GtkWidget *treeview;
79
80   GtkWidget *button_add;
81   GtkWidget *button_import;
82
83   GtkWidget *frame_new_account;
84   GtkWidget *combobox_protocol;
85   GtkWidget *hbox_type;
86   GtkWidget *button_create;
87   GtkWidget *button_back;
88   GtkWidget *radiobutton_reuse;
89   GtkWidget *radiobutton_register;
90
91   GtkWidget *image_type;
92   GtkWidget *label_name;
93   GtkWidget *label_type;
94   GtkWidget *settings_widget;
95
96   /* We have to keep a reference on the actual EmpathyAccountWidget, not just
97    * his GtkWidget. It is the only reliable source we can query to know if
98    * there are any unsaved changes to the currently selected account. We can't
99    * look at the account settings because it does not contain everything that
100    * can be changed using the EmpathyAccountWidget. For instance, it does not
101    * contain the state of the "Enabled" checkbox. */
102   EmpathyAccountWidget *setting_widget_object;
103
104   gboolean  connecting_show;
105   guint connecting_id;
106
107   gulong  settings_ready_id;
108   EmpathyAccountSettings *settings_ready;
109
110   EmpathyAccountManager *account_manager;
111   EmpathyConnectionManagers *cms;
112
113   GtkWindow *parent_window;
114   EmpathyAccount *initial_selection;
115
116   /* Those are needed when changing the selected row. When a user selects
117    * another account and there are unsaved changes on the currently selected
118    * one, a confirmation message box is presented to him. Since his answer
119    * is retrieved asynchronously, we keep some information as member of the
120    * EmpathyAccountsDialog object. */
121   gboolean force_change_row;
122   GtkTreeRowReference *destination_row;
123
124
125 } EmpathyAccountsDialogPriv;
126
127 enum {
128   COL_NAME,
129   COL_STATUS,
130   COL_ACCOUNT_POINTER,
131   COL_ACCOUNT_SETTINGS_POINTER,
132   COL_COUNT
133 };
134
135 enum {
136   PROP_PARENT = 1
137 };
138
139 static EmpathyAccountSettings * accounts_dialog_model_get_selected_settings (
140     EmpathyAccountsDialog *dialog);
141
142 static gboolean accounts_dialog_get_settings_iter (
143     EmpathyAccountsDialog *dialog,
144     EmpathyAccountSettings *settings,
145     GtkTreeIter *iter);
146
147 static void accounts_dialog_model_select_first (EmpathyAccountsDialog *dialog);
148
149 static void accounts_dialog_update (EmpathyAccountsDialog *dialog,
150     EmpathyAccountSettings *settings);
151
152 static void accounts_dialog_update_settings (EmpathyAccountsDialog *dialog,
153     EmpathyAccountSettings *settings);
154
155 static void
156 accounts_dialog_update_name_label (EmpathyAccountsDialog *dialog,
157     const gchar *display_name)
158 {
159   gchar *text;
160   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
161
162   text = g_markup_printf_escaped ("<big><b>%s</b></big>", display_name);
163   gtk_label_set_markup (GTK_LABEL (priv->label_name), text);
164
165   g_free (text);
166 }
167
168 static void
169 empathy_account_dialog_widget_cancelled_cb (
170     EmpathyAccountWidget *widget_object,
171     EmpathyAccountsDialog *dialog)
172 {
173   GtkTreeView *view;
174   GtkTreeModel *model;
175   GtkTreeSelection *selection;
176   GtkTreeIter iter;
177   EmpathyAccountSettings *settings;
178   EmpathyAccount *account;
179   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
180
181   view = GTK_TREE_VIEW (priv->treeview);
182   selection = gtk_tree_view_get_selection (view);
183
184   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
185     return;
186
187   gtk_tree_model_get (model, &iter,
188       COL_ACCOUNT_SETTINGS_POINTER, &settings,
189       COL_ACCOUNT_POINTER, &account, -1);
190
191   empathy_account_widget_discard_pending_changes (priv->setting_widget_object);
192
193   if (account == NULL)
194     {
195       /* We were creating an account. We remove the selected row */
196       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
197     }
198   else
199     {
200       /* We were modifying an account. We discard the changes by reloading the
201        * settings and the UI. */
202       accounts_dialog_update_settings (dialog, settings);
203       g_object_unref (account);
204     }
205
206   if (settings != NULL)
207     g_object_unref (settings);
208 }
209
210 static gchar *
211 get_default_display_name (EmpathyAccountSettings *settings)
212 {
213   const gchar *login_id;
214   const gchar *protocol, *p;
215   gchar *default_display_name;
216
217   login_id = empathy_account_settings_get_string (settings, "account");
218   protocol = empathy_account_settings_get_protocol (settings);
219
220   if ((p = empathy_protocol_name_to_display_name (protocol)) != NULL)
221     protocol = p;
222
223   if (login_id != NULL)
224     {
225       if (!tp_strdiff (protocol, "irc"))
226         {
227           const gchar* server;
228           server = empathy_account_settings_get_string (settings, "server");
229
230           /* To translators: The first parameter is the login id and the
231            * second one is the server. The resulting string will be something
232            * like: "MyUserName on chat.freenode.net".
233            * You should reverse the order of these arguments if the
234            * server should come before the login id in your locale.*/
235           default_display_name = g_strdup_printf (_("%1$s on %2$s"),
236               login_id, server);
237         }
238       else
239         {
240           default_display_name = g_strdup (login_id);
241         }
242     }
243   else if (protocol != NULL)
244     {
245       /* To translators: The parameter is the protocol name. The resulting
246        * string will be something like: "Jabber Account" */
247       default_display_name = g_strdup_printf (_("%s Account"), protocol);
248     }
249   else
250     {
251       default_display_name = g_strdup (_("New account"));
252     }
253
254   return default_display_name;
255 }
256
257 static void
258 empathy_account_dialog_account_created_cb (EmpathyAccountWidget *widget_object,
259     EmpathyAccountsDialog *dialog)
260 {
261   gchar *display_name;
262   EmpathyAccountSettings *settings =
263       accounts_dialog_model_get_selected_settings (dialog);
264
265   display_name = get_default_display_name (settings);
266
267   empathy_account_settings_set_display_name_async (settings,
268       display_name, NULL, NULL);
269
270   g_free (display_name);
271
272   accounts_dialog_update_settings (dialog, settings);
273
274   if (settings)
275     g_object_unref (settings);
276 }
277
278 static void
279 account_dialog_create_settings_widget (EmpathyAccountsDialog *dialog,
280     EmpathyAccountSettings *settings)
281 {
282   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
283   gchar *icon_name;
284
285   priv->setting_widget_object =
286       empathy_account_widget_new_for_protocol (settings, FALSE);
287
288   priv->settings_widget =
289       empathy_account_widget_get_widget (priv->setting_widget_object);
290
291   priv->settings_widget =
292       empathy_account_widget_get_widget (priv->setting_widget_object);
293   g_signal_connect (priv->setting_widget_object, "account-created",
294         G_CALLBACK (empathy_account_dialog_account_created_cb), dialog);
295   g_signal_connect (priv->setting_widget_object, "cancelled",
296           G_CALLBACK (empathy_account_dialog_widget_cancelled_cb), dialog);
297
298   gtk_container_add (GTK_CONTAINER (priv->alignment_settings),
299       priv->settings_widget);
300   gtk_widget_show (priv->settings_widget);
301
302   icon_name = empathy_account_settings_get_icon_name (settings);
303
304   if (!gtk_icon_theme_has_icon (gtk_icon_theme_get_default (),
305           icon_name))
306     /* show the default icon; keep this in sync with the default
307      * one in empathy-accounts-dialog.ui.
308      */
309     icon_name = GTK_STOCK_CUT;
310
311   gtk_image_set_from_icon_name (GTK_IMAGE (priv->image_type),
312       icon_name, GTK_ICON_SIZE_DIALOG);
313   gtk_widget_set_tooltip_text (priv->image_type,
314       empathy_protocol_name_to_display_name
315       (empathy_account_settings_get_protocol (settings)));
316   gtk_widget_show (priv->image_type);
317
318   accounts_dialog_update_name_label (dialog,
319       empathy_account_settings_get_display_name (settings));
320 }
321
322 static void
323 account_dialog_settings_ready_cb (EmpathyAccountSettings *settings,
324     GParamSpec *spec,
325     EmpathyAccountsDialog *dialog)
326 {
327   if (empathy_account_settings_is_ready (settings))
328     account_dialog_create_settings_widget (dialog, settings);
329 }
330
331 static void
332 accounts_dialog_model_select_first (EmpathyAccountsDialog *dialog)
333 {
334   GtkTreeView      *view;
335   GtkTreeModel     *model;
336   GtkTreeSelection *selection;
337   GtkTreeIter       iter;
338   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
339
340   /* select first */
341   view = GTK_TREE_VIEW (priv->treeview);
342   model = gtk_tree_view_get_model (view);
343
344   if (gtk_tree_model_get_iter_first (model, &iter))
345     {
346       selection = gtk_tree_view_get_selection (view);
347       gtk_tree_selection_select_iter (selection, &iter);
348     }
349   else
350     {
351       accounts_dialog_update_settings (dialog, NULL);
352     }
353 }
354
355 static gboolean
356 accounts_dialog_has_pending_change (EmpathyAccountsDialog *dialog,
357     EmpathyAccount **account)
358 {
359   GtkTreeIter iter;
360   GtkTreeModel *model;
361   GtkTreeSelection *selection;
362   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
363
364   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
365
366   if (gtk_tree_selection_get_selected (selection, &model, &iter))
367     gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, account, -1);
368
369   return *account != NULL && priv->setting_widget_object != NULL
370       && empathy_account_widget_contains_pending_changes (
371           priv->setting_widget_object);
372 }
373
374 static void
375 accounts_dialog_protocol_changed_cb (GtkWidget *widget,
376     EmpathyAccountsDialog *dialog)
377 {
378   TpConnectionManager *cm;
379   TpConnectionManagerProtocol *proto;
380   gboolean is_gtalk;
381   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
382
383   cm = empathy_protocol_chooser_dup_selected (
384       EMPATHY_PROTOCOL_CHOOSER (priv->combobox_protocol), &proto, &is_gtalk);
385
386   if (cm == NULL)
387     return;
388
389   if (proto == NULL)
390     {
391       g_object_unref (cm);
392       return;
393     }
394
395 #ifndef HAVE_MOBLIN
396   if (tp_connection_manager_protocol_can_register (proto) && !is_gtalk)
397     {
398       gtk_widget_show (priv->radiobutton_register);
399       gtk_widget_show (priv->radiobutton_reuse);
400     }
401   else
402     {
403       gtk_widget_hide (priv->radiobutton_register);
404       gtk_widget_hide (priv->radiobutton_reuse);
405     }
406 #else
407   gtk_widget_hide (priv->radiobutton_register);
408   gtk_widget_hide (priv->radiobutton_reuse);
409 #endif
410
411   g_object_unref (cm);
412 }
413
414 static void
415 accounts_dialog_setup_ui_to_add_account (EmpathyAccountsDialog *dialog)
416 {
417   GtkTreeView *view;
418   GtkTreeModel *model;
419   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
420
421   view = GTK_TREE_VIEW (priv->treeview);
422   model = gtk_tree_view_get_model (view);
423
424   gtk_widget_set_sensitive (priv->button_add, FALSE);
425   gtk_widget_hide (priv->vbox_details);
426   gtk_widget_hide (priv->frame_no_protocol);
427   gtk_widget_show (priv->frame_new_account);
428
429   /* If we have no account, no need of a back button */
430   if (gtk_tree_model_iter_n_children (model, NULL) > 0)
431     gtk_widget_show (priv->button_back);
432   else
433     gtk_widget_hide (priv->button_back);
434
435   accounts_dialog_protocol_changed_cb (priv->radiobutton_register, dialog);
436   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->radiobutton_reuse),
437       TRUE);
438   gtk_combo_box_set_active (GTK_COMBO_BOX (priv->combobox_protocol), 0);
439   gtk_widget_grab_focus (priv->combobox_protocol);
440 }
441
442 static void
443 accounts_dialog_show_question_dialog (EmpathyAccountsDialog *dialog,
444     const gchar *primary_text,
445     const gchar *secondary_text,
446     GCallback response_callback,
447     gpointer user_data,
448     const gchar *first_button_text,
449     ...)
450 {
451   va_list button_args;
452   GtkWidget *message_dialog;
453   const gchar *button_text;
454   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
455
456   message_dialog = gtk_message_dialog_new (GTK_WINDOW (priv->window),
457       GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
458       GTK_MESSAGE_QUESTION,
459       GTK_BUTTONS_NONE,
460       "%s", primary_text);
461
462   gtk_message_dialog_format_secondary_text (
463       GTK_MESSAGE_DIALOG (message_dialog), "%s", secondary_text);
464
465   va_start (button_args, first_button_text);
466   for (button_text = first_button_text;
467        button_text;
468        button_text = va_arg (button_args, const gchar *))
469     {
470       gint response_id;
471       response_id = va_arg (button_args, gint);
472
473       gtk_dialog_add_button (GTK_DIALOG (message_dialog), button_text,
474           response_id);
475     }
476   va_end (button_args);
477
478   g_signal_connect (message_dialog, "response", response_callback, user_data);
479
480   gtk_widget_show (message_dialog);
481 }
482
483 static void
484 accounts_dialog_add_pending_changes_response_cb (GtkDialog *message_dialog,
485   gint response_id,
486   gpointer *user_data)
487 {
488   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
489   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
490
491   gtk_widget_destroy (GTK_WIDGET (message_dialog));
492
493   if (response_id == GTK_RESPONSE_YES)
494     {
495       empathy_account_widget_discard_pending_changes (
496           priv->setting_widget_object);
497       accounts_dialog_setup_ui_to_add_account (dialog);
498     }
499 }
500
501 static void
502 accounts_dialog_button_add_clicked_cb (GtkWidget *button,
503     EmpathyAccountsDialog *dialog)
504 {
505   EmpathyAccount *account = NULL;
506
507   if (accounts_dialog_has_pending_change (dialog, &account))
508     {
509       gchar *question_dialog_primary_text = g_strdup_printf (
510           PENDING_CHANGES_QUESTION_PRIMARY_TEXT,
511           empathy_account_get_display_name (account));
512
513       accounts_dialog_show_question_dialog (dialog,
514           question_dialog_primary_text,
515           _("You are about to create a new account, which will discard\n"
516               "your changes. Are you sure you want to proceed?"),
517           G_CALLBACK (accounts_dialog_add_pending_changes_response_cb),
518           dialog,
519           GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
520           GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
521
522       g_free (question_dialog_primary_text);
523     }
524   else
525     {
526       accounts_dialog_setup_ui_to_add_account (dialog);
527     }
528 }
529
530 static void
531 accounts_dialog_update_settings (EmpathyAccountsDialog *dialog,
532     EmpathyAccountSettings *settings)
533 {
534   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
535
536   if (priv->settings_ready != NULL)
537     {
538       g_signal_handler_disconnect (priv->settings_ready,
539           priv->settings_ready_id);
540       priv->settings_ready = NULL;
541       priv->settings_ready_id = 0;
542     }
543
544   if (!settings)
545     {
546       GtkTreeView  *view;
547       GtkTreeModel *model;
548
549       view = GTK_TREE_VIEW (priv->treeview);
550       model = gtk_tree_view_get_model (view);
551
552       if (gtk_tree_model_iter_n_children (model, NULL) > 0)
553         {
554           /* We have configured accounts, select the first one */
555           accounts_dialog_model_select_first (dialog);
556           return;
557         }
558       if (empathy_connection_managers_get_cms_num (priv->cms) > 0)
559         {
560           /* We have no account configured but we have some
561            * profiles installed. The user obviously wants to add
562            * an account. Click on the Add button for him. */
563           accounts_dialog_button_add_clicked_cb (priv->button_add,
564               dialog);
565           return;
566         }
567
568       /* No account and no profile, warn the user */
569       gtk_widget_hide (priv->vbox_details);
570       gtk_widget_hide (priv->frame_new_account);
571       gtk_widget_show (priv->frame_no_protocol);
572       gtk_widget_set_sensitive (priv->button_add, FALSE);
573       return;
574     }
575
576   /* We have an account selected, destroy old settings and create a new
577    * one for the account selected */
578   gtk_widget_hide (priv->frame_new_account);
579   gtk_widget_hide (priv->frame_no_protocol);
580   gtk_widget_show (priv->vbox_details);
581   gtk_widget_set_sensitive (priv->button_add, TRUE);
582
583   if (priv->settings_widget)
584     {
585       gtk_widget_destroy (priv->settings_widget);
586       priv->settings_widget = NULL;
587     }
588
589   if (empathy_account_settings_is_ready (settings))
590     {
591       account_dialog_create_settings_widget (dialog, settings);
592     }
593   else
594     {
595       priv->settings_ready = settings;
596       priv->settings_ready_id =
597         g_signal_connect (settings, "notify::ready",
598             G_CALLBACK (account_dialog_settings_ready_cb), dialog);
599     }
600
601 }
602
603 static void
604 accounts_dialog_name_editing_started_cb (GtkCellRenderer *renderer,
605     GtkCellEditable *editable,
606     gchar *path,
607     EmpathyAccountsDialog *dialog)
608 {
609   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
610
611   if (priv->connecting_id)
612     g_source_remove (priv->connecting_id);
613
614   DEBUG ("Editing account name started; stopping flashing");
615 }
616
617 static void
618 accounts_dialog_model_pixbuf_data_func (GtkTreeViewColumn *tree_column,
619     GtkCellRenderer *cell,
620     GtkTreeModel *model,
621     GtkTreeIter *iter,
622     EmpathyAccountsDialog *dialog)
623 {
624   EmpathyAccountSettings  *settings;
625   gchar              *icon_name;
626   GdkPixbuf          *pixbuf;
627   TpConnectionStatus  status;
628   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
629
630   gtk_tree_model_get (model, iter,
631       COL_STATUS, &status,
632       COL_ACCOUNT_SETTINGS_POINTER, &settings,
633       -1);
634
635   icon_name = empathy_account_settings_get_icon_name (settings);
636   pixbuf = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON);
637
638   if (pixbuf)
639     {
640       if (status == TP_CONNECTION_STATUS_DISCONNECTED ||
641           (status == TP_CONNECTION_STATUS_CONNECTING &&
642               !priv->connecting_show))
643         {
644           GdkPixbuf *modded_pixbuf;
645
646           modded_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
647               TRUE,
648               8,
649               gdk_pixbuf_get_width (pixbuf),
650               gdk_pixbuf_get_height (pixbuf));
651
652           gdk_pixbuf_saturate_and_pixelate (pixbuf,
653               modded_pixbuf,
654               1.0,
655               TRUE);
656           g_object_unref (pixbuf);
657           pixbuf = modded_pixbuf;
658         }
659     }
660
661   g_object_set (cell,
662       "visible", TRUE,
663       "pixbuf", pixbuf,
664       NULL);
665
666   g_object_unref (settings);
667
668   if (pixbuf)
669     g_object_unref (pixbuf);
670 }
671
672 static gboolean
673 accounts_dialog_row_changed_foreach (GtkTreeModel *model,
674     GtkTreePath *path,
675     GtkTreeIter *iter,
676     gpointer user_data)
677 {
678   gtk_tree_model_row_changed (model, path, iter);
679
680   return FALSE;
681 }
682
683 static gboolean
684 accounts_dialog_flash_connecting_cb (EmpathyAccountsDialog *dialog)
685 {
686   GtkTreeView  *view;
687   GtkTreeModel *model;
688   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
689
690   priv->connecting_show = !priv->connecting_show;
691
692   view = GTK_TREE_VIEW (priv->treeview);
693   model = gtk_tree_view_get_model (view);
694
695   gtk_tree_model_foreach (model, accounts_dialog_row_changed_foreach, NULL);
696
697   return TRUE;
698 }
699
700 static void
701 accounts_dialog_name_edited_cb (GtkCellRendererText *renderer,
702     gchar *path,
703     gchar *new_text,
704     EmpathyAccountsDialog *dialog)
705 {
706   EmpathyAccountSettings    *settings;
707   GtkTreeModel *model;
708   GtkTreePath  *treepath;
709   GtkTreeIter   iter;
710   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
711
712   if (empathy_account_manager_get_connecting_accounts
713       (priv->account_manager) > 0)
714     {
715       priv->connecting_id = g_timeout_add (FLASH_TIMEOUT,
716           (GSourceFunc) accounts_dialog_flash_connecting_cb,
717           dialog);
718     }
719
720   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
721   treepath = gtk_tree_path_new_from_string (path);
722   gtk_tree_model_get_iter (model, &iter, treepath);
723   gtk_tree_model_get (model, &iter,
724       COL_ACCOUNT_SETTINGS_POINTER, &settings,
725       -1);
726   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
727       COL_NAME, new_text,
728       -1);
729   gtk_tree_path_free (treepath);
730
731   empathy_account_settings_set_display_name_async (settings, new_text,
732       NULL, NULL);
733   g_object_set (settings, "display-name-overridden", TRUE, NULL);
734   g_object_unref (settings);
735 }
736
737 static void
738 accounts_dialog_delete_account_response_cb (GtkDialog *message_dialog,
739   gint response_id,
740   gpointer user_data)
741 {
742   EmpathyAccount *account;
743   GtkTreeModel *model;
744   GtkTreeIter iter;
745   GtkTreeSelection *selection;
746   EmpathyAccountsDialog *account_dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
747   EmpathyAccountsDialogPriv *priv = GET_PRIV (account_dialog);
748
749   if (response_id == GTK_RESPONSE_YES)
750     {
751       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
752
753       if (!gtk_tree_selection_get_selected (selection, &model, &iter))
754         return;
755
756       gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, &account, -1);
757
758       if (account != NULL)
759         {
760           empathy_account_remove_async (account, NULL, NULL);
761           g_object_unref (account);
762           account = NULL;
763         }
764
765       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
766       accounts_dialog_model_select_first (account_dialog);
767     }
768
769   gtk_widget_destroy (GTK_WIDGET (message_dialog));
770 }
771
772 static void
773 accounts_dialog_view_delete_activated_cb (EmpathyCellRendererActivatable *cell,
774     const gchar *path_string,
775     EmpathyAccountsDialog *dialog)
776 {
777   EmpathyAccount *account;
778   GtkTreeModel *model;
779   GtkTreeIter iter;
780   gchar *question_dialog_primary_text;
781   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
782
783   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
784
785   if (!gtk_tree_model_get_iter_from_string (model, &iter, path_string))
786     return;
787
788   gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, &account, -1);
789
790   if (account == NULL || !empathy_account_is_valid (account))
791     {
792       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
793       accounts_dialog_model_select_first (dialog);
794       return;
795     }
796
797   question_dialog_primary_text = g_strdup_printf (
798 #ifndef HAVE_MOBLIN
799       _("You are about to remove your %s account!\n"
800           "Are you sure you want to proceed?"),
801 #else
802       /* Translators: this is used only when built on a moblin platform */
803       _("Do you want to remove %s from your computer?"),
804 #endif /* HAVE_MOBLIN */
805       empathy_account_get_display_name (account));
806
807   accounts_dialog_show_question_dialog (dialog, question_dialog_primary_text,
808 #ifndef HAVE_MOBLIN
809       _("Any associated conversations and chat rooms will NOT be "
810           "removed if you decide to proceed.\n"
811           "\n"
812           "Should you decide to add the account back at a later time, "
813           "they will still be available."),
814 #else
815       /* Translators: this is used only when built on a moblin platform */
816       _("This will not remove your account on the server."),
817 #endif /* HAVE_MOBLIN */
818       G_CALLBACK (accounts_dialog_delete_account_response_cb),
819       dialog,
820       GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
821       GTK_STOCK_REMOVE, GTK_RESPONSE_YES, NULL);
822
823   g_free (question_dialog_primary_text);
824
825   if (account != NULL)
826     {
827       g_object_unref (account);
828       account = NULL;
829     }
830 }
831
832 static void
833 accounts_dialog_model_add_columns (EmpathyAccountsDialog *dialog)
834 {
835   GtkTreeView       *view;
836   GtkTreeViewColumn *column;
837   GtkCellRenderer   *cell;
838   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
839
840   view = GTK_TREE_VIEW (priv->treeview);
841   gtk_tree_view_set_headers_visible (view, FALSE);
842
843   /* Account column */
844   column = gtk_tree_view_column_new ();
845   gtk_tree_view_column_set_expand (column, TRUE);
846   gtk_tree_view_append_column (view, column);
847
848   /* Icon renderer */
849   cell = gtk_cell_renderer_pixbuf_new ();
850   gtk_tree_view_column_pack_start (column, cell, FALSE);
851   gtk_tree_view_column_set_cell_data_func (column, cell,
852       (GtkTreeCellDataFunc)
853       accounts_dialog_model_pixbuf_data_func,
854       dialog,
855       NULL);
856 #ifdef HAVE_MOBLIN
857   g_object_set (cell, "ypad", 4, NULL);
858 #endif
859
860   /* Name renderer */
861   cell = gtk_cell_renderer_text_new ();
862   g_object_set (cell,
863       "ellipsize", PANGO_ELLIPSIZE_END,
864       "width-chars", 25,
865       "editable", TRUE,
866       NULL);
867   gtk_tree_view_column_pack_start (column, cell, TRUE);
868   gtk_tree_view_column_add_attribute (column, cell, "text", COL_NAME);
869   g_signal_connect (cell, "edited",
870       G_CALLBACK (accounts_dialog_name_edited_cb),
871       dialog);
872   g_signal_connect (cell, "editing-started",
873       G_CALLBACK (accounts_dialog_name_editing_started_cb),
874       dialog);
875
876   /* Delete column */
877   cell = empathy_cell_renderer_activatable_new ();
878   gtk_tree_view_column_pack_start (column, cell, FALSE);
879   g_object_set (cell,
880         "icon-name", GTK_STOCK_DELETE,
881 #ifdef HAVE_MOBLIN
882         "show-on-select", TRUE,
883 #endif
884         NULL);
885
886   g_signal_connect (cell, "path-activated",
887       G_CALLBACK (accounts_dialog_view_delete_activated_cb),
888       dialog);
889 }
890
891 static EmpathyAccountSettings *
892 accounts_dialog_model_get_selected_settings (EmpathyAccountsDialog *dialog)
893 {
894   GtkTreeView      *view;
895   GtkTreeModel     *model;
896   GtkTreeSelection *selection;
897   GtkTreeIter       iter;
898   EmpathyAccountSettings   *settings;
899   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
900
901   view = GTK_TREE_VIEW (priv->treeview);
902   selection = gtk_tree_view_get_selection (view);
903
904   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
905     return NULL;
906
907   gtk_tree_model_get (model, &iter,
908       COL_ACCOUNT_SETTINGS_POINTER, &settings, -1);
909
910   return settings;
911 }
912
913 static void
914 accounts_dialog_model_selection_changed (GtkTreeSelection *selection,
915     EmpathyAccountsDialog *dialog)
916 {
917   EmpathyAccountSettings *settings;
918   GtkTreeModel *model;
919   GtkTreeIter   iter;
920   gboolean      is_selection;
921
922   is_selection = gtk_tree_selection_get_selected (selection, &model, &iter);
923
924   settings = accounts_dialog_model_get_selected_settings (dialog);
925   accounts_dialog_update_settings (dialog, settings);
926
927   if (settings != NULL)
928     g_object_unref (settings);
929 }
930
931 static void
932 accounts_dialog_selection_change_response_cb (GtkDialog *message_dialog,
933   gint response_id,
934   gpointer *user_data)
935 {
936   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
937   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
938
939   gtk_widget_destroy (GTK_WIDGET (message_dialog));
940
941     if (response_id == GTK_RESPONSE_YES && priv->destination_row != NULL)
942       {
943         /* The user wants to lose unsaved changes to the currently selected
944          * account and select another account. We discard the changes and
945          * select the other account. */
946         GtkTreePath *path;
947         GtkTreeSelection *selection;
948
949         priv->force_change_row = TRUE;
950         empathy_account_widget_discard_pending_changes (
951             priv->setting_widget_object);
952
953         path = gtk_tree_row_reference_get_path (priv->destination_row);
954         selection = gtk_tree_view_get_selection (
955             GTK_TREE_VIEW (priv->treeview));
956
957         if (path != NULL)
958           {
959             /* This will trigger a call to
960              * accounts_dialog_account_selection_change() */
961             gtk_tree_selection_select_path (selection, path);
962             gtk_tree_path_free (path);
963           }
964
965         gtk_tree_row_reference_free (priv->destination_row);
966       }
967     else
968       {
969         priv->force_change_row = FALSE;
970       }
971 }
972
973 static gboolean
974 accounts_dialog_account_selection_change (GtkTreeSelection *selection,
975     GtkTreeModel *model,
976     GtkTreePath *path,
977     gboolean path_currently_selected,
978     gpointer data)
979 {
980   EmpathyAccount *account = NULL;
981   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (data);
982   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
983
984   if (priv->force_change_row)
985     {
986       /* We came back here because the user wants to discard changes to his
987        * modified account. The changes have already been discarded so we
988        * just change the selected row. */
989       priv->force_change_row = FALSE;
990       return TRUE;
991     }
992
993   if (accounts_dialog_has_pending_change (dialog, &account))
994     {
995       /* The currently selected account has some unsaved changes. We ask
996        * the user if he really wants to lose his changes and select another
997        * account */
998       gchar *question_dialog_primary_text;
999       priv->destination_row = gtk_tree_row_reference_new (model, path);
1000
1001       question_dialog_primary_text = g_strdup_printf (
1002           PENDING_CHANGES_QUESTION_PRIMARY_TEXT,
1003           empathy_account_get_display_name (account));
1004
1005       accounts_dialog_show_question_dialog (dialog,
1006           question_dialog_primary_text,
1007           _("You are about to select another account, which will discard\n"
1008               "your changes. Are you sure you want to proceed?"),
1009           G_CALLBACK (accounts_dialog_selection_change_response_cb),
1010           dialog,
1011           GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1012           GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
1013
1014       g_free (question_dialog_primary_text);
1015     }
1016   else
1017     {
1018       return TRUE;
1019     }
1020
1021   return FALSE;
1022 }
1023
1024 static void
1025 accounts_dialog_model_setup (EmpathyAccountsDialog *dialog)
1026 {
1027   GtkListStore     *store;
1028   GtkTreeSelection *selection;
1029   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1030
1031   store = gtk_list_store_new (COL_COUNT,
1032       G_TYPE_STRING,         /* name */
1033       G_TYPE_UINT,           /* status */
1034       EMPATHY_TYPE_ACCOUNT,   /* account */
1035       EMPATHY_TYPE_ACCOUNT_SETTINGS); /* settings */
1036
1037   gtk_tree_view_set_model (GTK_TREE_VIEW (priv->treeview),
1038       GTK_TREE_MODEL (store));
1039
1040   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1041   gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
1042   gtk_tree_selection_set_select_function (selection,
1043       accounts_dialog_account_selection_change, dialog, NULL);
1044
1045   g_signal_connect (selection, "changed",
1046       G_CALLBACK (accounts_dialog_model_selection_changed),
1047       dialog);
1048
1049   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
1050       COL_NAME, GTK_SORT_ASCENDING);
1051
1052   accounts_dialog_model_add_columns (dialog);
1053
1054   g_object_unref (store);
1055 }
1056
1057 static gboolean
1058 accounts_dialog_get_settings_iter (EmpathyAccountsDialog *dialog,
1059     EmpathyAccountSettings *settings,
1060     GtkTreeIter *iter)
1061 {
1062   GtkTreeView      *view;
1063   GtkTreeSelection *selection;
1064   GtkTreeModel     *model;
1065   gboolean          ok;
1066   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1067
1068   /* Update the status in the model */
1069   view = GTK_TREE_VIEW (priv->treeview);
1070   selection = gtk_tree_view_get_selection (view);
1071   model = gtk_tree_view_get_model (view);
1072
1073   for (ok = gtk_tree_model_get_iter_first (model, iter);
1074        ok;
1075        ok = gtk_tree_model_iter_next (model, iter))
1076     {
1077       EmpathyAccountSettings *this_settings;
1078       gboolean   equal;
1079
1080       gtk_tree_model_get (model, iter,
1081           COL_ACCOUNT_SETTINGS_POINTER, &this_settings,
1082           -1);
1083
1084       equal = (this_settings == settings);
1085       g_object_unref (this_settings);
1086
1087       if (equal)
1088         return TRUE;
1089     }
1090
1091   return FALSE;
1092 }
1093
1094 static gboolean
1095 accounts_dialog_get_account_iter (EmpathyAccountsDialog *dialog,
1096     EmpathyAccount *account,
1097     GtkTreeIter *iter)
1098 {
1099   GtkTreeView      *view;
1100   GtkTreeSelection *selection;
1101   GtkTreeModel     *model;
1102   gboolean          ok;
1103   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1104
1105   /* Update the status in the model */
1106   view = GTK_TREE_VIEW (priv->treeview);
1107   selection = gtk_tree_view_get_selection (view);
1108   model = gtk_tree_view_get_model (view);
1109
1110   for (ok = gtk_tree_model_get_iter_first (model, iter);
1111        ok;
1112        ok = gtk_tree_model_iter_next (model, iter))
1113     {
1114       EmpathyAccountSettings *settings;
1115       gboolean   equal;
1116
1117       gtk_tree_model_get (model, iter,
1118           COL_ACCOUNT_SETTINGS_POINTER, &settings,
1119           -1);
1120
1121       equal = empathy_account_settings_has_account (settings, account);
1122       g_object_unref (settings);
1123
1124       if (equal)
1125         return TRUE;
1126     }
1127
1128   return FALSE;
1129 }
1130
1131 static void
1132 accounts_dialog_model_set_selected (EmpathyAccountsDialog *dialog,
1133     EmpathyAccountSettings *settings)
1134 {
1135   GtkTreeSelection *selection;
1136   GtkTreeIter       iter;
1137   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1138
1139   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1140   if (accounts_dialog_get_settings_iter (dialog, settings, &iter))
1141     gtk_tree_selection_select_iter (selection, &iter);
1142 }
1143 static void
1144 accounts_dialog_add (EmpathyAccountsDialog *dialog,
1145     EmpathyAccountSettings *settings)
1146 {
1147   GtkTreeModel       *model;
1148   GtkTreeIter         iter;
1149   const gchar        *name;
1150   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1151
1152   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1153   name = empathy_account_settings_get_display_name (settings);
1154
1155   gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1156
1157   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1158       COL_NAME, name,
1159       COL_STATUS, TP_CONNECTION_STATUS_DISCONNECTED,
1160       COL_ACCOUNT_SETTINGS_POINTER, settings,
1161       -1);
1162 }
1163
1164 static void
1165 accounts_dialog_connection_changed_cb     (EmpathyAccountManager *manager,
1166     EmpathyAccount *account,
1167     TpConnectionStatusReason reason,
1168     TpConnectionStatus current,
1169     TpConnectionStatus previous,
1170     EmpathyAccountsDialog *dialog)
1171 {
1172   GtkTreeModel *model;
1173   GtkTreeIter   iter;
1174   gboolean      found;
1175   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1176
1177   /* Update the status in the model */
1178   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1179
1180   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1181     {
1182       GtkTreePath *path;
1183
1184       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1185           COL_STATUS, current,
1186           -1);
1187
1188       path = gtk_tree_model_get_path (model, &iter);
1189       gtk_tree_model_row_changed (model, path, &iter);
1190       gtk_tree_path_free (path);
1191     }
1192
1193   found = (empathy_account_manager_get_connecting_accounts (manager) > 0);
1194
1195   if (!found && priv->connecting_id)
1196     {
1197       g_source_remove (priv->connecting_id);
1198       priv->connecting_id = 0;
1199     }
1200
1201   if (found && !priv->connecting_id)
1202     priv->connecting_id = g_timeout_add (FLASH_TIMEOUT,
1203         (GSourceFunc) accounts_dialog_flash_connecting_cb,
1204         dialog);
1205 }
1206
1207 static void
1208 accounts_dialog_account_display_name_changed_cb (EmpathyAccount *account,
1209   GParamSpec *pspec,
1210   gpointer user_data)
1211 {
1212   const gchar *display_name;
1213   GtkTreeIter iter;
1214   GtkTreeModel *model;
1215   EmpathyAccountSettings *settings;
1216   EmpathyAccount *selected_account;
1217   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1218   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1219
1220   display_name = empathy_account_get_display_name (account);
1221   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1222   settings = accounts_dialog_model_get_selected_settings (dialog);
1223   if (settings == NULL)
1224     return;
1225
1226   selected_account = empathy_account_settings_get_account (settings);
1227
1228   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1229     {
1230       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1231           COL_NAME, display_name,
1232           -1);
1233     }
1234
1235   if (selected_account == account)
1236     accounts_dialog_update_name_label (dialog, display_name);
1237
1238   g_object_unref (settings);
1239 }
1240
1241 static void
1242 accounts_dialog_add_account (EmpathyAccountsDialog *dialog,
1243     EmpathyAccount *account)
1244 {
1245   EmpathyAccountSettings *settings;
1246   GtkTreeModel       *model;
1247   GtkTreeIter         iter;
1248   TpConnectionStatus  status;
1249   const gchar        *name;
1250   gboolean            enabled;
1251   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1252
1253   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1254   g_object_get (account, "connection-status", &status, NULL);
1255   name = empathy_account_get_display_name (account);
1256   enabled = empathy_account_is_enabled (account);
1257
1258   settings = empathy_account_settings_new_for_account (account);
1259
1260   if (!accounts_dialog_get_account_iter (dialog, account, &iter))
1261     gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1262
1263   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1264       COL_NAME, name,
1265       COL_STATUS, status,
1266       COL_ACCOUNT_POINTER, account,
1267       COL_ACCOUNT_SETTINGS_POINTER, settings,
1268       -1);
1269
1270   accounts_dialog_connection_changed_cb (priv->account_manager,
1271       account,
1272       TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED,
1273       status,
1274       TP_CONNECTION_STATUS_DISCONNECTED,
1275       dialog);
1276
1277   empathy_signal_connect_weak (account, "notify::display-name",
1278       G_CALLBACK (accounts_dialog_account_display_name_changed_cb),
1279       G_OBJECT (dialog));
1280
1281   g_object_unref (settings);
1282 }
1283
1284 static void
1285 accounts_dialog_update (EmpathyAccountsDialog *dialog,
1286     EmpathyAccountSettings *settings)
1287 {
1288   GtkTreeModel       *model;
1289   GtkTreeIter         iter;
1290   TpConnectionStatus  status = TP_CONNECTION_STATUS_DISCONNECTED;
1291   const gchar        *name;
1292   gboolean            enabled = FALSE;
1293   EmpathyAccount     *account;
1294   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1295
1296   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1297   name = empathy_account_settings_get_display_name (settings);
1298
1299   account = empathy_account_settings_get_account (settings);
1300   if (account != NULL)
1301     {
1302       enabled = empathy_account_is_enabled (account);
1303       g_object_get (account, "connection-status", &status, NULL);
1304     }
1305
1306   accounts_dialog_get_settings_iter (dialog, settings, &iter);
1307   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1308       COL_NAME, name,
1309       COL_STATUS, status,
1310       COL_ACCOUNT_POINTER, account,
1311       COL_ACCOUNT_SETTINGS_POINTER, settings,
1312       -1);
1313 }
1314
1315 static void
1316 accounts_dialog_account_added_cb (EmpathyAccountManager *manager,
1317     EmpathyAccount *account,
1318     EmpathyAccountsDialog *dialog)
1319 {
1320   accounts_dialog_add_account (dialog, account);
1321 }
1322
1323
1324 static void
1325 accounts_dialog_account_removed_cb (EmpathyAccountManager *manager,
1326     EmpathyAccount *account,
1327     EmpathyAccountsDialog *dialog)
1328 {
1329   GtkTreeIter iter;
1330   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1331
1332   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1333     {
1334       gtk_list_store_remove (GTK_LIST_STORE (
1335             gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview))), &iter);
1336     }
1337 }
1338
1339 static void
1340 enable_or_disable_account (EmpathyAccountsDialog *dialog,
1341     EmpathyAccount *account,
1342     gboolean enabled)
1343 {
1344   GtkTreeModel *model;
1345   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1346
1347   /* Update the status in the model */
1348   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1349
1350   DEBUG ("Account %s is now %s",
1351       empathy_account_get_display_name (account),
1352       enabled ? "enabled" : "disabled");
1353 }
1354
1355 static void
1356 accounts_dialog_account_disabled_cb (EmpathyAccountManager *manager,
1357     EmpathyAccount *account,
1358     EmpathyAccountsDialog *dialog)
1359 {
1360   enable_or_disable_account (dialog, account, FALSE);
1361 }
1362
1363 static void
1364 accounts_dialog_account_enabled_cb (EmpathyAccountManager *manager,
1365     EmpathyAccount *account,
1366     EmpathyAccountsDialog *dialog)
1367 {
1368   enable_or_disable_account (dialog, account, TRUE);
1369 }
1370
1371 static void
1372 accounts_dialog_account_changed_cb (EmpathyAccountManager *manager,
1373     EmpathyAccount *account,
1374     EmpathyAccountsDialog  *dialog)
1375 {
1376   EmpathyAccountSettings *settings, *selected_settings;
1377   GtkTreeModel *model;
1378   GtkTreeIter iter;
1379   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1380
1381   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1382
1383   if (!accounts_dialog_get_account_iter (dialog, account, &iter))
1384     return;
1385
1386   gtk_tree_model_get (model, &iter,
1387       COL_ACCOUNT_SETTINGS_POINTER, &settings,
1388       -1);
1389
1390   accounts_dialog_update (dialog, settings);
1391   selected_settings = accounts_dialog_model_get_selected_settings (dialog);
1392
1393   if (settings == selected_settings)
1394     accounts_dialog_update_name_label (dialog,
1395         empathy_account_settings_get_display_name (settings));
1396
1397   if (settings)
1398     g_object_unref (settings);
1399
1400   if (selected_settings)
1401     g_object_unref (selected_settings);
1402 }
1403
1404 static void
1405 accounts_dialog_button_create_clicked_cb (GtkWidget *button,
1406     EmpathyAccountsDialog *dialog)
1407 {
1408   EmpathyAccountSettings *settings;
1409   gchar *str;
1410   const gchar *display_name;
1411   TpConnectionManager *cm;
1412   TpConnectionManagerProtocol *proto;
1413   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1414   gboolean is_gtalk;
1415
1416   cm = empathy_protocol_chooser_dup_selected (
1417       EMPATHY_PROTOCOL_CHOOSER (priv->combobox_protocol), &proto, &is_gtalk);
1418
1419   display_name = empathy_protocol_name_to_display_name (
1420       is_gtalk ? "gtalk" : proto->name);
1421
1422   if (display_name == NULL)
1423     display_name = proto->name;
1424
1425   /* Create account */
1426   /* To translator: %s is the name of the protocol, such as "Google Talk" or
1427    * "Yahoo!"
1428    */
1429   str = g_strdup_printf (_("New %s account"), display_name);
1430   settings = empathy_account_settings_new (cm->name, proto->name, str);
1431
1432   g_free (str);
1433
1434 #ifndef HAVE_MOBLIN
1435   if (tp_connection_manager_protocol_can_register (proto))
1436     {
1437       gboolean active;
1438
1439       active = gtk_toggle_button_get_active
1440         (GTK_TOGGLE_BUTTON (priv->radiobutton_register));
1441       if (active)
1442         empathy_account_settings_set_boolean (settings, "register", TRUE);
1443     }
1444 #endif
1445
1446   if (is_gtalk)
1447     empathy_account_settings_set_icon_name_async (settings, "im-google-talk",
1448         NULL, NULL);
1449
1450   accounts_dialog_add (dialog, settings);
1451   accounts_dialog_model_set_selected (dialog, settings);
1452
1453   g_object_unref (settings);
1454   g_object_unref (cm);
1455 }
1456
1457 static void
1458 accounts_dialog_button_back_clicked_cb (GtkWidget *button,
1459     EmpathyAccountsDialog *dialog)
1460 {
1461   EmpathyAccountSettings *settings;
1462
1463   settings = accounts_dialog_model_get_selected_settings (dialog);
1464   accounts_dialog_update_settings (dialog, settings);
1465
1466   if (settings)
1467     g_object_unref (settings);
1468 }
1469
1470 static void
1471 accounts_dialog_button_help_clicked_cb (GtkWidget *button,
1472     EmpathyAccountsDialog *dialog)
1473 {
1474   empathy_url_show (button, "ghelp:empathy?accounts-window");
1475 }
1476
1477 static void
1478 accounts_dialog_button_import_clicked_cb (GtkWidget *button,
1479     EmpathyAccountsDialog *dialog)
1480 {
1481   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1482   GtkWidget *import_dialog;
1483
1484   import_dialog = empathy_import_dialog_new (GTK_WINDOW (priv->window),
1485       FALSE);
1486   gtk_widget_show (import_dialog);
1487 }
1488
1489 static void
1490 accounts_dialog_close_response_cb (GtkDialog *message_dialog,
1491   gint response_id,
1492   gpointer user_data)
1493 {
1494   GtkWidget *account_dialog = GTK_WIDGET (user_data);
1495
1496   gtk_widget_destroy (GTK_WIDGET (message_dialog));
1497
1498   if (response_id == GTK_RESPONSE_YES)
1499     gtk_widget_destroy (account_dialog);
1500 }
1501
1502 static void
1503 accounts_dialog_response_cb (GtkWidget *widget,
1504     gint response,
1505     EmpathyAccountsDialog *dialog)
1506 {
1507   EmpathyAccount *account = NULL;
1508
1509   if (accounts_dialog_has_pending_change (dialog, &account))
1510     {
1511       gchar *question_dialog_primary_text;
1512       question_dialog_primary_text = g_strdup_printf (
1513           PENDING_CHANGES_QUESTION_PRIMARY_TEXT,
1514           empathy_account_get_display_name (account));
1515
1516       accounts_dialog_show_question_dialog (dialog,
1517           question_dialog_primary_text,
1518           _("You are about to close the window, which will discard\n"
1519               "your changes. Are you sure you want to proceed?"),
1520           G_CALLBACK (accounts_dialog_close_response_cb),
1521           widget,
1522           GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1523           GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
1524
1525       g_free (question_dialog_primary_text);
1526     }
1527   else if (response == GTK_RESPONSE_CLOSE ||
1528            response == GTK_RESPONSE_DELETE_EVENT)
1529     gtk_widget_destroy (widget);
1530 }
1531
1532 static gboolean
1533 accounts_dialog_delete_event_cb (GtkWidget *widget,
1534     GdkEvent *event,
1535     EmpathyAccountsDialog *dialog)
1536 {
1537   /* we maunally handle responses to delete events */
1538   return TRUE;
1539 }
1540
1541 static void
1542 accounts_dialog_destroy_cb (GtkObject *obj,
1543     EmpathyAccountsDialog *dialog)
1544 {
1545   DEBUG ("%p", obj);
1546
1547   g_object_unref (dialog);
1548 }
1549
1550 static void
1551 accounts_dialog_set_selected_account (EmpathyAccountsDialog *dialog,
1552     EmpathyAccount *account)
1553 {
1554   GtkTreeSelection *selection;
1555   GtkTreeIter       iter;
1556   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1557
1558   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1559   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1560     gtk_tree_selection_select_iter (selection, &iter);
1561 }
1562
1563 static void
1564 accounts_dialog_cms_ready_cb (EmpathyConnectionManagers *cms,
1565     GParamSpec *pspec,
1566     EmpathyAccountsDialog *dialog)
1567 {
1568   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1569
1570   if (empathy_connection_managers_is_ready (cms))
1571     {
1572       accounts_dialog_update_settings (dialog, NULL);
1573
1574       if (priv->initial_selection != NULL)
1575         {
1576           accounts_dialog_set_selected_account
1577               (dialog, priv->initial_selection);
1578           g_object_unref (priv->initial_selection);
1579           priv->initial_selection = NULL;
1580         }
1581     }
1582 }
1583
1584 static void
1585 accounts_dialog_accounts_setup (EmpathyAccountsDialog *dialog)
1586 {
1587   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1588   GList *accounts, *l;
1589
1590   g_signal_connect (priv->account_manager, "account-created",
1591       G_CALLBACK (accounts_dialog_account_added_cb),
1592       dialog);
1593   g_signal_connect (priv->account_manager, "account-deleted",
1594       G_CALLBACK (accounts_dialog_account_removed_cb),
1595       dialog);
1596   g_signal_connect (priv->account_manager, "account-enabled",
1597       G_CALLBACK (accounts_dialog_account_enabled_cb),
1598       dialog);
1599   g_signal_connect (priv->account_manager, "account-disabled",
1600       G_CALLBACK (accounts_dialog_account_disabled_cb),
1601       dialog);
1602   g_signal_connect (priv->account_manager, "account-changed",
1603       G_CALLBACK (accounts_dialog_account_changed_cb),
1604       dialog);
1605   g_signal_connect (priv->account_manager, "account-connection-changed",
1606       G_CALLBACK (accounts_dialog_connection_changed_cb),
1607       dialog);
1608
1609   /* Add existing accounts */
1610   accounts = empathy_account_manager_dup_accounts (priv->account_manager);
1611   for (l = accounts; l; l = l->next)
1612     {
1613       accounts_dialog_add_account (dialog, l->data);
1614       g_object_unref (l->data);
1615     }
1616   g_list_free (accounts);
1617
1618   priv->cms = empathy_connection_managers_dup_singleton ();
1619   if (!empathy_connection_managers_is_ready (priv->cms))
1620     g_signal_connect (priv->cms, "notify::ready",
1621         G_CALLBACK (accounts_dialog_cms_ready_cb), dialog);
1622
1623   accounts_dialog_model_select_first (dialog);
1624 }
1625
1626 static void
1627 accounts_dialog_manager_ready_cb (EmpathyAccountManager *manager,
1628     GParamSpec *pspec,
1629     gpointer user_data)
1630 {
1631   if (!empathy_account_manager_is_ready (manager))
1632     return;
1633
1634   accounts_dialog_accounts_setup (user_data);
1635 }
1636
1637 static void
1638 accounts_dialog_build_ui (EmpathyAccountsDialog *dialog)
1639 {
1640   GtkBuilder                   *gui;
1641   gchar                        *filename;
1642   EmpathyAccountsDialogPriv    *priv = GET_PRIV (dialog);
1643 #ifdef HAVE_MOBLIN
1644   GtkWidget                    *action_area;
1645 #endif
1646
1647   filename = empathy_file_lookup ("empathy-accounts-dialog.ui", "src");
1648
1649   gui = empathy_builder_get_file (filename,
1650       "accounts_dialog", &priv->window,
1651       "vbox_details", &priv->vbox_details,
1652       "frame_no_protocol", &priv->frame_no_protocol,
1653       "alignment_settings", &priv->alignment_settings,
1654       "treeview", &priv->treeview,
1655       "frame_new_account", &priv->frame_new_account,
1656       "hbox_type", &priv->hbox_type,
1657       "button_create", &priv->button_create,
1658       "button_back", &priv->button_back,
1659       "radiobutton_reuse", &priv->radiobutton_reuse,
1660       "radiobutton_register", &priv->radiobutton_register,
1661       "image_type", &priv->image_type,
1662       "label_name", &priv->label_name,
1663       "button_add", &priv->button_add,
1664       "button_import", &priv->button_import,
1665       NULL);
1666   g_free (filename);
1667
1668   empathy_builder_connect (gui, dialog,
1669       "accounts_dialog", "response", accounts_dialog_response_cb,
1670       "accounts_dialog", "destroy", accounts_dialog_destroy_cb,
1671       "accounts_dialog", "delete-event", accounts_dialog_delete_event_cb,
1672       "button_create", "clicked", accounts_dialog_button_create_clicked_cb,
1673       "button_back", "clicked", accounts_dialog_button_back_clicked_cb,
1674       "button_add", "clicked", accounts_dialog_button_add_clicked_cb,
1675       "button_help", "clicked", accounts_dialog_button_help_clicked_cb,
1676       "button_import", "clicked", accounts_dialog_button_import_clicked_cb,
1677       NULL);
1678
1679   g_object_unref (gui);
1680
1681 #ifdef HAVE_MOBLIN
1682   action_area = gtk_dialog_get_action_area (GTK_DIALOG (priv->window));
1683   gtk_widget_hide (action_area);
1684
1685   /* Translators: this is used only when built on a moblin platform */
1686   gtk_button_set_label (GTK_BUTTON (priv->button_create), _("_Next"));
1687   gtk_button_set_use_underline (GTK_BUTTON (priv->button_create), TRUE);
1688 #endif
1689
1690   priv->combobox_protocol = empathy_protocol_chooser_new ();
1691   gtk_box_pack_start (GTK_BOX (priv->hbox_type),
1692       priv->combobox_protocol,
1693       TRUE, TRUE, 0);
1694   gtk_widget_show (priv->combobox_protocol);
1695   g_signal_connect (priv->combobox_protocol, "changed",
1696       G_CALLBACK (accounts_dialog_protocol_changed_cb),
1697       dialog);
1698
1699   if (priv->parent_window)
1700     gtk_window_set_transient_for (GTK_WINDOW (priv->window),
1701         priv->parent_window);
1702 }
1703
1704 static void
1705 do_dispose (GObject *obj)
1706 {
1707   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (obj);
1708   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1709
1710   /* Disconnect signals */
1711   g_signal_handlers_disconnect_by_func (priv->account_manager,
1712       accounts_dialog_account_added_cb,
1713       dialog);
1714   g_signal_handlers_disconnect_by_func (priv->account_manager,
1715       accounts_dialog_account_removed_cb,
1716       dialog);
1717   g_signal_handlers_disconnect_by_func (priv->account_manager,
1718       accounts_dialog_account_enabled_cb,
1719       dialog);
1720   g_signal_handlers_disconnect_by_func (priv->account_manager,
1721       accounts_dialog_account_disabled_cb,
1722       dialog);
1723   g_signal_handlers_disconnect_by_func (priv->account_manager,
1724       accounts_dialog_account_changed_cb,
1725       dialog);
1726   g_signal_handlers_disconnect_by_func (priv->account_manager,
1727       accounts_dialog_connection_changed_cb,
1728       dialog);
1729   g_signal_handlers_disconnect_by_func (priv->account_manager,
1730       accounts_dialog_manager_ready_cb,
1731       dialog);
1732
1733   if (priv->connecting_id)
1734     g_source_remove (priv->connecting_id);
1735
1736   if (priv->account_manager != NULL)
1737     {
1738       g_object_unref (priv->account_manager);
1739       priv->account_manager = NULL;
1740     }
1741
1742   if (priv->cms != NULL)
1743     {
1744       g_object_unref (priv->cms);
1745       priv->cms = NULL;
1746     }
1747
1748   if (priv->initial_selection != NULL)
1749     g_object_unref (priv->initial_selection);
1750   priv->initial_selection = NULL;
1751
1752   G_OBJECT_CLASS (empathy_accounts_dialog_parent_class)->dispose (obj);
1753 }
1754
1755 static GObject *
1756 do_constructor (GType type,
1757     guint n_props,
1758     GObjectConstructParam *props)
1759 {
1760   GObject *retval;
1761
1762   if (dialog_singleton)
1763     {
1764       retval = G_OBJECT (dialog_singleton);
1765       /* We don't ref the object as the caller is not suppose to unref it.
1766        * The dialog is unreffed in accounts_dialog_destroy_cb when the window
1767        * has been destroyed. */
1768     }
1769   else
1770     {
1771       retval =
1772         G_OBJECT_CLASS (empathy_accounts_dialog_parent_class)->constructor
1773             (type, n_props, props);
1774
1775       dialog_singleton = EMPATHY_ACCOUNTS_DIALOG (retval);
1776       g_object_add_weak_pointer (retval, (gpointer) &dialog_singleton);
1777     }
1778
1779   return retval;
1780 }
1781
1782 static void
1783 do_get_property (GObject *object,
1784     guint property_id,
1785     GValue *value,
1786     GParamSpec *pspec)
1787 {
1788   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
1789
1790   switch (property_id)
1791     {
1792     case PROP_PARENT:
1793       g_value_set_object (value, priv->parent_window);
1794       break;
1795     default:
1796       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1797     }
1798 }
1799
1800 static void
1801 do_set_property (GObject *object,
1802     guint property_id,
1803     const GValue *value,
1804     GParamSpec *pspec)
1805 {
1806   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
1807
1808   switch (property_id)
1809     {
1810     case PROP_PARENT:
1811       priv->parent_window = g_value_get_object (value);
1812       break;
1813     default:
1814       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1815     }
1816 }
1817
1818 static void
1819 do_constructed (GObject *object)
1820 {
1821   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (object);
1822   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1823   gboolean import_asked;
1824
1825   accounts_dialog_build_ui (dialog);
1826   accounts_dialog_model_setup (dialog);
1827
1828   /* Set up signalling */
1829   priv->account_manager = empathy_account_manager_dup_singleton ();
1830
1831   if (!empathy_account_manager_is_ready (priv->account_manager))
1832     g_signal_connect (priv->account_manager, "notify::ready",
1833         G_CALLBACK (accounts_dialog_manager_ready_cb), dialog);
1834   else
1835     accounts_dialog_accounts_setup (dialog);
1836
1837   empathy_conf_get_bool (empathy_conf_get (),
1838       EMPATHY_PREFS_IMPORT_ASKED, &import_asked);
1839
1840   if (empathy_import_accounts_to_import ())
1841     {
1842       gtk_widget_show (priv->button_import);
1843
1844       if (!import_asked)
1845         {
1846           GtkWidget *import_dialog;
1847
1848           empathy_conf_set_bool (empathy_conf_get (),
1849               EMPATHY_PREFS_IMPORT_ASKED, TRUE);
1850           import_dialog = empathy_import_dialog_new (GTK_WINDOW (priv->window),
1851               FALSE);
1852           gtk_widget_show (import_dialog);
1853         }
1854     }
1855 }
1856
1857 static void
1858 empathy_accounts_dialog_class_init (EmpathyAccountsDialogClass *klass)
1859 {
1860   GObjectClass *oclass = G_OBJECT_CLASS (klass);
1861   GParamSpec *param_spec;
1862
1863   oclass->constructor = do_constructor;
1864   oclass->dispose = do_dispose;
1865   oclass->constructed = do_constructed;
1866   oclass->set_property = do_set_property;
1867   oclass->get_property = do_get_property;
1868
1869   param_spec = g_param_spec_object ("parent",
1870       "parent", "The parent window",
1871       GTK_TYPE_WINDOW,
1872       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
1873   g_object_class_install_property (oclass, PROP_PARENT, param_spec);
1874
1875   g_type_class_add_private (klass, sizeof (EmpathyAccountsDialogPriv));
1876 }
1877
1878 static void
1879 empathy_accounts_dialog_init (EmpathyAccountsDialog *dialog)
1880 {
1881   EmpathyAccountsDialogPriv *priv;
1882
1883   priv = G_TYPE_INSTANCE_GET_PRIVATE ((dialog),
1884       EMPATHY_TYPE_ACCOUNTS_DIALOG,
1885       EmpathyAccountsDialogPriv);
1886   dialog->priv = priv;
1887 }
1888
1889 /* public methods */
1890
1891 GtkWidget *
1892 empathy_accounts_dialog_show (GtkWindow *parent,
1893     EmpathyAccount *selected_account)
1894 {
1895   EmpathyAccountsDialog *dialog;
1896   EmpathyAccountsDialogPriv *priv;
1897
1898   dialog = g_object_new (EMPATHY_TYPE_ACCOUNTS_DIALOG,
1899       "parent", parent, NULL);
1900
1901   priv = GET_PRIV (dialog);
1902
1903   if (selected_account)
1904     {
1905       if (empathy_connection_managers_is_ready (priv->cms))
1906         accounts_dialog_set_selected_account (dialog, selected_account);
1907       else
1908         /* save the selection to set it later when the cms
1909          * becomes ready.
1910          */
1911         priv->initial_selection = g_object_ref (selected_account);
1912     }
1913
1914   gtk_window_present (GTK_WINDOW (priv->window));
1915
1916   return priv->window;
1917 }