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