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