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