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