]> git.0d.be Git - empathy.git/blob - src/empathy-accounts-dialog.c
Use library i18n method for source files included in shared library
[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-lib.h>
33 #include <dbus/dbus-glib.h>
34
35 #include <telepathy-glib/account-manager.h>
36 #include <telepathy-glib/defs.h>
37 #include <telepathy-glib/util.h>
38
39 #include <libempathy/empathy-utils.h>
40 #include <libempathy/empathy-connection-managers.h>
41 #include <libempathy/empathy-connectivity.h>
42 #include <libempathy-gtk/empathy-ui-utils.h>
43
44 #include <libempathy-gtk/empathy-protocol-chooser.h>
45 #include <libempathy-gtk/empathy-account-widget.h>
46 #include <libempathy-gtk/empathy-account-widget-irc.h>
47 #include <libempathy-gtk/empathy-account-widget-sip.h>
48 #include <libempathy-gtk/empathy-cell-renderer-activatable.h>
49 #include <libempathy-gtk/empathy-conf.h>
50 #include <libempathy-gtk/empathy-images.h>
51
52 #include "empathy-accounts-dialog.h"
53 #include "empathy-import-dialog.h"
54 #include "empathy-import-utils.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 modifications to 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, GTK_TYPE_DIALOG);
73
74 typedef struct {
75   GtkWidget *alignment_settings;
76   GtkWidget *alignment_infobar;
77
78   GtkWidget *vbox_details;
79   GtkWidget *infobar;
80   GtkWidget *label_status;
81   GtkWidget *image_status;
82   GtkWidget *throbber;
83   GtkWidget *frame_no_protocol;
84
85   GtkWidget *treeview;
86
87   GtkWidget *button_add;
88   GtkWidget *button_remove;
89   GtkWidget *button_import;
90
91   GtkWidget *combobox_protocol;
92   GtkWidget *hbox_protocol;
93
94   GtkWidget *image_type;
95   GtkWidget *label_name;
96   GtkWidget *label_type;
97   GtkWidget *settings_widget;
98
99   /* We have to keep a reference on the actual EmpathyAccountWidget, not just
100    * his GtkWidget. It is the only reliable source we can query to know if
101    * there are any unsaved changes to the currently selected account. We can't
102    * look at the account settings because it does not contain everything that
103    * can be changed using the EmpathyAccountWidget. For instance, it does not
104    * contain the state of the "Enabled" checkbox. */
105   EmpathyAccountWidget *setting_widget_object;
106
107   gboolean  connecting_show;
108   guint connecting_id;
109
110   gulong  settings_ready_id;
111   EmpathyAccountSettings *settings_ready;
112
113   TpAccountManager *account_manager;
114   EmpathyConnectionManagers *cms;
115
116   GtkWindow *parent_window;
117   TpAccount *initial_selection;
118
119   /* Those are needed when changing the selected row. When a user selects
120    * another account and there are unsaved changes on the currently selected
121    * one, a confirmation message box is presented to him. Since his answer
122    * is retrieved asynchronously, we keep some information as member of the
123    * EmpathyAccountsDialog object. */
124   gboolean force_change_row;
125   GtkTreeRowReference *destination_row;
126
127   gboolean dispose_has_run;
128 } EmpathyAccountsDialogPriv;
129
130 enum {
131   COL_NAME,
132   COL_STATUS,
133   COL_ACCOUNT,
134   COL_ACCOUNT_SETTINGS,
135   COL_COUNT
136 };
137
138 enum {
139   PROP_PARENT = 1
140 };
141
142 static EmpathyAccountSettings * accounts_dialog_model_get_selected_settings (
143     EmpathyAccountsDialog *dialog);
144
145 static gboolean accounts_dialog_get_settings_iter (
146     EmpathyAccountsDialog *dialog,
147     EmpathyAccountSettings *settings,
148     GtkTreeIter *iter);
149
150 static void accounts_dialog_model_select_first (EmpathyAccountsDialog *dialog);
151
152 static void accounts_dialog_update_settings (EmpathyAccountsDialog *dialog,
153     EmpathyAccountSettings *settings);
154
155 static void accounts_dialog_add (EmpathyAccountsDialog *dialog,
156     EmpathyAccountSettings *settings);
157
158 static void accounts_dialog_model_set_selected (EmpathyAccountsDialog *dialog,
159     EmpathyAccountSettings *settings);
160
161 static void accounts_dialog_connection_changed_cb (TpAccount *account,
162     guint old_status,
163     guint current,
164     guint reason,
165     gchar *dbus_error_name,
166     GHashTable *details,
167     EmpathyAccountsDialog *dialog);
168
169 static void accounts_dialog_presence_changed_cb (TpAccount *account,
170     guint presence,
171     gchar *status,
172     gchar *status_message,
173     EmpathyAccountsDialog *dialog);
174
175 static void accounts_dialog_model_selection_changed (
176     GtkTreeSelection *selection,
177     EmpathyAccountsDialog *dialog);
178
179 static void
180 accounts_dialog_update_name_label (EmpathyAccountsDialog *dialog,
181     const gchar *display_name)
182 {
183   gchar *text;
184   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
185
186   text = g_markup_printf_escaped ("<b>%s</b>", display_name);
187   gtk_label_set_markup (GTK_LABEL (priv->label_name), text);
188
189   g_free (text);
190 }
191
192 static void
193 accounts_dialog_update_status_infobar (EmpathyAccountsDialog *dialog,
194     TpAccount *account)
195 {
196   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
197   const gchar               *message;
198   gchar                     *message_markup;
199   gchar                     *status_message = NULL;
200   guint                     status;
201   guint                     reason;
202   guint                     presence;
203   EmpathyConnectivity       *connectivity;
204   GtkTreeView               *view;
205   GtkTreeModel              *model;
206   GtkTreeSelection          *selection;
207   GtkTreeIter               iter;
208   TpAccount                 *selected_account;
209   gboolean                  account_enabled;
210   gboolean                  creating_account;
211
212   view = GTK_TREE_VIEW (priv->treeview);
213   selection = gtk_tree_view_get_selection (view);
214
215   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
216     return;
217
218   gtk_tree_model_get (model, &iter, COL_ACCOUNT, &selected_account, -1);
219   if (selected_account != NULL)
220     g_object_unref (selected_account);
221
222   /* do not update the infobar when the account is not selected */
223   if (account != selected_account)
224     return;
225
226   if (account != NULL)
227     {
228       status = tp_account_get_connection_status (account, &reason);
229       presence = tp_account_get_current_presence (account, NULL, &status_message);
230       account_enabled = tp_account_is_enabled (account);
231       creating_account = FALSE;
232
233       if (status == TP_CONNECTION_STATUS_CONNECTED &&
234           (presence == TP_CONNECTION_PRESENCE_TYPE_OFFLINE ||
235            presence == TP_CONNECTION_PRESENCE_TYPE_UNSET))
236         /* If presence is Unset (CM doesn't implement SimplePresence) but we
237          * are connected, consider ourself as Available.
238          * We also check Offline because of this MC5 bug: fd.o #26060 */
239         presence = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
240
241       /* set presence to offline if account is disabled
242        * (else no icon is shown in infobar)*/
243       if (!account_enabled)
244         presence = TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
245     }
246   else
247     {
248       status = TP_CONNECTION_STATUS_DISCONNECTED;
249       presence = TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
250       account_enabled = FALSE;
251       creating_account = TRUE;
252     }
253
254   gtk_image_set_from_icon_name (GTK_IMAGE (priv->image_status),
255       empathy_icon_name_for_presence (presence), GTK_ICON_SIZE_SMALL_TOOLBAR);
256
257   if (account_enabled)
258     {
259       switch (status)
260         {
261           case TP_CONNECTION_STATUS_CONNECTING:
262             message = _("Connecting…");
263             gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
264                 GTK_MESSAGE_INFO);
265
266             gtk_spinner_start (GTK_SPINNER (priv->throbber));
267             gtk_widget_show (priv->throbber);
268             gtk_widget_hide (priv->image_status);
269             break;
270           case TP_CONNECTION_STATUS_CONNECTED:
271             if (g_strcmp0 (status_message, "") == 0)
272               {
273                 message = g_strdup_printf ("%s",
274                     empathy_presence_get_default_message (presence));
275               }
276             else
277               {
278                 message = g_strdup_printf ("%s — %s",
279                     empathy_presence_get_default_message (presence),
280                     status_message);
281               }
282             gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
283                 GTK_MESSAGE_INFO);
284
285             gtk_widget_show (priv->image_status);
286             gtk_widget_hide (priv->throbber);
287             break;
288           case TP_CONNECTION_STATUS_DISCONNECTED:
289             message = g_strdup_printf (_("Disconnected — %s"),
290                 empathy_status_reason_get_default_message (reason));
291
292             if (reason == TP_CONNECTION_STATUS_REASON_REQUESTED)
293               {
294                 message = g_strdup_printf (_("Offline — %s"),
295                     empathy_status_reason_get_default_message (reason));
296                 gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
297                     GTK_MESSAGE_WARNING);
298               }
299             else
300               {
301                 gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
302                     GTK_MESSAGE_ERROR);
303               }
304
305             connectivity = empathy_connectivity_dup_singleton ();
306             if (!empathy_connectivity_is_online (connectivity))
307                message = _("Offline — No Network Connection");
308
309             g_object_unref (connectivity);
310             gtk_spinner_stop (GTK_SPINNER (priv->throbber));
311             gtk_widget_show (priv->image_status);
312             gtk_widget_hide (priv->throbber);
313             break;
314           default:
315             message = _("Unknown Status");
316             gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
317                 GTK_MESSAGE_WARNING);
318
319             gtk_spinner_stop (GTK_SPINNER (priv->throbber));
320             gtk_widget_hide (priv->image_status);
321             gtk_widget_hide (priv->throbber);
322         }
323     }
324   else
325     {
326       message = _("Offline — Account Disabled");
327
328       gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar),
329           GTK_MESSAGE_WARNING);
330       gtk_spinner_stop (GTK_SPINNER (priv->throbber));
331       gtk_widget_show (priv->image_status);
332       gtk_widget_hide (priv->throbber);
333     }
334
335   message_markup = g_markup_printf_escaped ("<i>%s</i>", message);
336   gtk_label_set_markup (GTK_LABEL (priv->label_status), message_markup);
337   gtk_widget_show (priv->label_status);
338
339   if (!creating_account)
340     gtk_widget_show (priv->infobar);
341   else
342     gtk_widget_hide (priv->infobar);
343
344   g_free (status_message);
345   g_free (message_markup);
346 }
347
348 void
349 empathy_account_dialog_cancel (EmpathyAccountsDialog *dialog)
350 {
351   GtkTreeView *view;
352   GtkTreeModel *model;
353   GtkTreeSelection *selection;
354   GtkTreeIter iter;
355   EmpathyAccountSettings *settings;
356   TpAccount *account;
357   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
358
359   view = GTK_TREE_VIEW (priv->treeview);
360   selection = gtk_tree_view_get_selection (view);
361
362   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
363     return;
364
365   gtk_tree_model_get (model, &iter,
366       COL_ACCOUNT_SETTINGS, &settings,
367       COL_ACCOUNT, &account, -1);
368
369   empathy_account_widget_discard_pending_changes (priv->setting_widget_object);
370
371   if (account == NULL)
372     {
373       /* We were creating an account. We remove the selected row */
374       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
375     }
376   else
377     {
378       /* We were modifying an account. We discard the changes by reloading the
379        * settings and the UI. */
380       accounts_dialog_update_settings (dialog, settings);
381       g_object_unref (account);
382     }
383
384   gtk_widget_set_sensitive (priv->treeview, TRUE);
385   gtk_widget_set_sensitive (priv->button_add, TRUE);
386   gtk_widget_set_sensitive (priv->button_remove, TRUE);
387   gtk_widget_set_sensitive (priv->button_import, TRUE);
388
389   if (settings != NULL)
390     g_object_unref (settings);
391 }
392
393 static void
394 empathy_account_dialog_widget_cancelled_cb (
395     EmpathyAccountWidget *widget_object,
396     EmpathyAccountsDialog *dialog)
397 {
398   empathy_account_dialog_cancel (dialog);
399 }
400
401 static void
402 empathy_account_dialog_account_created_cb (EmpathyAccountWidget *widget_object,
403     TpAccount *account,
404     EmpathyAccountsDialog *dialog)
405 {
406   EmpathyAccountSettings *settings =
407       accounts_dialog_model_get_selected_settings (dialog);
408   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
409
410   accounts_dialog_update_settings (dialog, settings);
411   accounts_dialog_update_status_infobar (dialog,
412       empathy_account_settings_get_account (settings));
413
414   gtk_widget_set_sensitive (priv->treeview, TRUE);
415   gtk_widget_set_sensitive (priv->button_add, TRUE);
416   gtk_widget_set_sensitive (priv->button_remove, TRUE);
417   gtk_widget_set_sensitive (priv->button_import, TRUE);
418
419   if (settings)
420     g_object_unref (settings);
421 }
422
423 static gboolean
424 accounts_dialog_has_valid_accounts (EmpathyAccountsDialog *dialog)
425 {
426   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
427   GtkTreeModel *model;
428   GtkTreeIter iter;
429   gboolean creating;
430
431   g_object_get (priv->setting_widget_object,
432       "creating-account", &creating, NULL);
433
434   if (!creating)
435     return TRUE;
436
437   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
438
439   if (gtk_tree_model_get_iter_first (model, &iter))
440     return gtk_tree_model_iter_next (model, &iter);
441
442   return FALSE;
443 }
444
445 static void
446 account_dialog_create_settings_widget (EmpathyAccountsDialog *dialog,
447     EmpathyAccountSettings *settings)
448 {
449   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
450   gchar                     *icon_name;
451   TpAccount                 *account;
452
453   priv->setting_widget_object =
454       empathy_account_widget_new_for_protocol (settings, FALSE);
455
456   if (accounts_dialog_has_valid_accounts (dialog))
457     empathy_account_widget_set_other_accounts_exist (
458         priv->setting_widget_object, TRUE);
459
460   priv->settings_widget =
461       empathy_account_widget_get_widget (priv->setting_widget_object);
462
463   g_signal_connect (priv->setting_widget_object, "account-created",
464         G_CALLBACK (empathy_account_dialog_account_created_cb), dialog);
465   g_signal_connect (priv->setting_widget_object, "cancelled",
466           G_CALLBACK (empathy_account_dialog_widget_cancelled_cb), dialog);
467
468   gtk_container_add (GTK_CONTAINER (priv->alignment_settings),
469       priv->settings_widget);
470   gtk_widget_show (priv->settings_widget);
471
472   icon_name = empathy_account_settings_get_icon_name (settings);
473
474   if (!gtk_icon_theme_has_icon (gtk_icon_theme_get_default (),
475           icon_name))
476     /* show the default icon; keep this in sync with the default
477      * one in empathy-accounts-dialog.ui.
478      */
479     icon_name = GTK_STOCK_CUT;
480
481   gtk_image_set_from_icon_name (GTK_IMAGE (priv->image_type),
482       icon_name, GTK_ICON_SIZE_DIALOG);
483   gtk_widget_set_tooltip_text (priv->image_type,
484       empathy_protocol_name_to_display_name
485       (empathy_account_settings_get_protocol (settings)));
486   gtk_widget_show (priv->image_type);
487
488   accounts_dialog_update_name_label (dialog,
489       empathy_account_settings_get_display_name (settings));
490
491   account = empathy_account_settings_get_account (settings);
492   accounts_dialog_update_status_infobar (dialog, account);
493 }
494
495 static void
496 account_dialog_settings_ready_cb (EmpathyAccountSettings *settings,
497     GParamSpec *spec,
498     EmpathyAccountsDialog *dialog)
499 {
500   if (empathy_account_settings_is_ready (settings))
501     account_dialog_create_settings_widget (dialog, settings);
502 }
503
504 static void
505 accounts_dialog_model_select_first (EmpathyAccountsDialog *dialog)
506 {
507   GtkTreeView      *view;
508   GtkTreeModel     *model;
509   GtkTreeSelection *selection;
510   GtkTreeIter       iter;
511   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
512
513   /* select first */
514   view = GTK_TREE_VIEW (priv->treeview);
515   model = gtk_tree_view_get_model (view);
516
517   if (gtk_tree_model_get_iter_first (model, &iter))
518     {
519       selection = gtk_tree_view_get_selection (view);
520       gtk_tree_selection_select_iter (selection, &iter);
521     }
522   else
523     {
524       accounts_dialog_update_settings (dialog, NULL);
525     }
526 }
527
528 static gboolean
529 accounts_dialog_has_pending_change (EmpathyAccountsDialog *dialog,
530     TpAccount **account)
531 {
532   GtkTreeIter iter;
533   GtkTreeModel *model;
534   GtkTreeSelection *selection;
535   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
536
537   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
538
539   if (gtk_tree_selection_get_selected (selection, &model, &iter))
540     gtk_tree_model_get (model, &iter, COL_ACCOUNT, account, -1);
541
542   return priv->setting_widget_object != NULL
543       && empathy_account_widget_contains_pending_changes (
544           priv->setting_widget_object);
545 }
546
547 static void
548 accounts_dialog_setup_ui_to_add_account (EmpathyAccountsDialog *dialog)
549 {
550   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
551   EmpathyAccountSettings *settings;
552   gchar *str;
553   const gchar *name, *display_name;
554   TpConnectionManager *cm;
555   TpConnectionManagerProtocol *proto;
556   gboolean is_gtalk, is_facebook;
557
558   cm = empathy_protocol_chooser_dup_selected (
559       EMPATHY_PROTOCOL_CHOOSER (priv->combobox_protocol), &proto, &is_gtalk,
560       &is_facebook);
561   if (cm == NULL)
562     return;
563
564   if (is_gtalk)
565     name = "gtalk";
566   else if (is_facebook)
567     name ="facebook";
568   else
569     name = proto->name;
570
571   display_name = empathy_protocol_name_to_display_name (name);
572   if (display_name == NULL)
573     display_name = proto->name;
574
575   /* Create account */
576   /* To translator: %s is the name of the protocol, such as "Google Talk" or
577    * "Yahoo!"
578    */
579   str = g_strdup_printf (_("New %s account"), display_name);
580   settings = empathy_account_settings_new (cm->name, proto->name, str);
581
582   g_free (str);
583
584   if (is_gtalk)
585     {
586       empathy_account_settings_set_icon_name_async (settings, "im-google-talk",
587           NULL, NULL);
588       /* We should not have to set the server but that may cause issue with
589        * buggy router. */
590       empathy_account_settings_set_string (settings, "server",
591           "talk.google.com");
592     }
593   else if (is_facebook)
594     {
595       empathy_account_settings_set_icon_name_async (settings, "im-facebook",
596           NULL, NULL);
597     }
598
599   accounts_dialog_add (dialog, settings);
600   accounts_dialog_model_set_selected (dialog, settings);
601
602   gtk_widget_show_all (priv->hbox_protocol);
603
604   g_object_unref (settings);
605   g_object_unref (cm);
606 }
607
608 static void
609 accounts_dialog_protocol_changed_cb (GtkWidget *widget,
610     EmpathyAccountsDialog *dialog)
611 {
612   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
613   GtkTreeSelection *selection;
614   GtkTreeModel *model;
615   GtkTreeIter iter;
616   gboolean creating;
617   EmpathyAccountSettings *settings;
618   gchar *account = NULL, *password = NULL;
619
620   /* The "changed" signal is fired during the initiation of the
621    * EmpathyProtocolChooser while populating the widget. Such signals should
622    * be ignored so we check if we are actually creating a new account. */
623   if (priv->setting_widget_object == NULL)
624     return;
625
626   g_object_get (priv->setting_widget_object,
627       "creating-account", &creating, NULL);
628   if (!creating)
629     return;
630
631   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
632
633   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
634     return;
635
636   /* Save "account" and "password" parameters */
637   g_object_get (priv->setting_widget_object, "settings", &settings, NULL);
638
639   if (settings != NULL)
640     {
641       account = g_strdup (empathy_account_settings_get_string (settings,
642             "account"));
643       password = g_strdup (empathy_account_settings_get_string (settings,
644             "password"));
645       g_object_unref (settings);
646     }
647
648   /* We are creating a new widget to replace the current one, don't ask
649    * confirmation to the user. */
650   priv->force_change_row = TRUE;
651
652   /* We'll update the selection after we create the new account widgets;
653    * updating it right now causes problems for the # of accounts = zero case */
654   g_signal_handlers_block_by_func (selection,
655       accounts_dialog_model_selection_changed, dialog);
656
657   gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
658
659   g_signal_handlers_unblock_by_func (selection,
660       accounts_dialog_model_selection_changed, dialog);
661
662   accounts_dialog_setup_ui_to_add_account (dialog);
663
664   /* Restore "account" and "password" parameters in the new widget */
665   if (account != NULL)
666     {
667       empathy_account_widget_set_account_param (priv->setting_widget_object,
668           account);
669       g_free (account);
670     }
671
672   if (password != NULL)
673     {
674       empathy_account_widget_set_password_param (priv->setting_widget_object,
675           password);
676       g_free (password);
677     }
678 }
679
680 static void
681 accounts_dialog_show_question_dialog (EmpathyAccountsDialog *dialog,
682     const gchar *primary_text,
683     const gchar *secondary_text,
684     GCallback response_callback,
685     gpointer user_data,
686     const gchar *first_button_text,
687     ...)
688 {
689   va_list button_args;
690   GtkWidget *message_dialog;
691   const gchar *button_text;
692
693   message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
694       GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
695       GTK_MESSAGE_QUESTION,
696       GTK_BUTTONS_NONE,
697       "%s", primary_text);
698
699   gtk_message_dialog_format_secondary_text (
700       GTK_MESSAGE_DIALOG (message_dialog), "%s", secondary_text);
701
702   va_start (button_args, first_button_text);
703   for (button_text = first_button_text;
704        button_text;
705        button_text = va_arg (button_args, const gchar *))
706     {
707       gint response_id;
708       response_id = va_arg (button_args, gint);
709
710       gtk_dialog_add_button (GTK_DIALOG (message_dialog), button_text,
711           response_id);
712     }
713   va_end (button_args);
714
715   g_signal_connect (message_dialog, "response", response_callback, user_data);
716
717   gtk_widget_show (message_dialog);
718 }
719
720 static void
721 accounts_dialog_add_pending_changes_response_cb (GtkDialog *message_dialog,
722   gint response_id,
723   gpointer *user_data)
724 {
725   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
726   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
727
728   gtk_widget_destroy (GTK_WIDGET (message_dialog));
729
730   if (response_id == GTK_RESPONSE_YES)
731     {
732       empathy_account_widget_discard_pending_changes (
733           priv->setting_widget_object);
734       accounts_dialog_setup_ui_to_add_account (dialog);
735     }
736 }
737
738 static gchar *
739 get_dialog_primary_text (TpAccount *account)
740 {
741   if (account != NULL)
742     {
743       /* Existing account */
744       return g_strdup_printf (PENDING_CHANGES_QUESTION_PRIMARY_TEXT,
745           tp_account_get_display_name (account));
746     }
747   else
748     {
749       /* Newly created account */
750       return g_strdup (UNSAVED_NEW_ACCOUNT_QUESTION_PRIMARY_TEXT);
751     }
752 }
753
754 static void
755 accounts_dialog_button_add_clicked_cb (GtkWidget *button,
756     EmpathyAccountsDialog *dialog)
757 {
758   TpAccount *account = NULL;
759   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
760
761   if (accounts_dialog_has_pending_change (dialog, &account))
762     {
763       gchar *question_dialog_primary_text = get_dialog_primary_text (account);
764
765       accounts_dialog_show_question_dialog (dialog,
766           question_dialog_primary_text,
767           _("You are about to create a new account, which will discard\n"
768               "your changes. Are you sure you want to proceed?"),
769           G_CALLBACK (accounts_dialog_add_pending_changes_response_cb),
770           dialog,
771           GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
772           GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
773
774       g_free (question_dialog_primary_text);
775     }
776   else
777     {
778       accounts_dialog_setup_ui_to_add_account (dialog);
779       gtk_widget_set_sensitive (priv->treeview, FALSE);
780       gtk_widget_set_sensitive (priv->button_add, FALSE);
781       gtk_widget_set_sensitive (priv->button_remove, FALSE);
782       gtk_widget_set_sensitive (priv->button_import, FALSE);
783     }
784
785   if (account != NULL)
786     g_object_unref (account);
787 }
788
789 static void
790 accounts_dialog_update_settings (EmpathyAccountsDialog *dialog,
791     EmpathyAccountSettings *settings)
792 {
793   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
794
795   if (priv->settings_ready != NULL)
796     {
797       g_signal_handler_disconnect (priv->settings_ready,
798           priv->settings_ready_id);
799       priv->settings_ready = NULL;
800       priv->settings_ready_id = 0;
801     }
802
803   if (!settings)
804     {
805       GtkTreeView  *view;
806       GtkTreeModel *model;
807       GtkTreeSelection *selection;
808
809       view = GTK_TREE_VIEW (priv->treeview);
810       model = gtk_tree_view_get_model (view);
811       selection = gtk_tree_view_get_selection (view);
812
813       if (gtk_tree_model_iter_n_children (model, NULL) > 0)
814         {
815           /* We have configured accounts, select the first one if there
816            * is no other account selected already. */
817           if (!gtk_tree_selection_get_selected (selection, NULL, NULL))
818             accounts_dialog_model_select_first (dialog);
819
820           return;
821         }
822       if (empathy_connection_managers_get_cms_num (priv->cms) > 0)
823         {
824           /* We have no account configured but we have some
825            * profiles installed. The user obviously wants to add
826            * an account. Click on the Add button for him. */
827           accounts_dialog_button_add_clicked_cb (priv->button_add,
828               dialog);
829           return;
830         }
831
832       /* No account and no profile, warn the user */
833       gtk_widget_hide (priv->vbox_details);
834       gtk_widget_show (priv->frame_no_protocol);
835       gtk_widget_set_sensitive (priv->button_add, FALSE);
836       return;
837     }
838
839   /* We have an account selected, destroy old settings and create a new
840    * one for the account selected */
841   gtk_widget_hide (priv->frame_no_protocol);
842   gtk_widget_show (priv->vbox_details);
843   gtk_widget_hide (priv->hbox_protocol);
844
845   if (priv->settings_widget)
846     {
847       gtk_widget_destroy (priv->settings_widget);
848       priv->settings_widget = NULL;
849     }
850
851   if (empathy_account_settings_is_ready (settings))
852     {
853       account_dialog_create_settings_widget (dialog, settings);
854     }
855   else
856     {
857       priv->settings_ready = settings;
858       priv->settings_ready_id =
859         g_signal_connect (settings, "notify::ready",
860             G_CALLBACK (account_dialog_settings_ready_cb), dialog);
861     }
862
863 }
864
865 static void
866 accounts_dialog_name_editing_started_cb (GtkCellRenderer *renderer,
867     GtkCellEditable *editable,
868     gchar *path,
869     EmpathyAccountsDialog *dialog)
870 {
871   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
872
873   if (priv->connecting_id)
874     g_source_remove (priv->connecting_id);
875
876   DEBUG ("Editing account name started; stopping flashing");
877 }
878
879 static const gchar *
880 get_status_icon_for_account (EmpathyAccountsDialog *self,
881     TpAccount *account)
882 {
883   EmpathyAccountsDialogPriv *priv = GET_PRIV (self);
884   TpConnectionStatus status;
885   TpConnectionStatusReason reason;
886   TpConnectionPresenceType presence;
887
888   if (account == NULL)
889     return empathy_icon_name_for_presence (TP_CONNECTION_PRESENCE_TYPE_OFFLINE);
890
891   if (!tp_account_is_enabled (account))
892     return empathy_icon_name_for_presence (TP_CONNECTION_PRESENCE_TYPE_OFFLINE);
893
894   status = tp_account_get_connection_status (account, &reason);
895
896   if (status == TP_CONNECTION_STATUS_DISCONNECTED)
897     {
898       if (reason != TP_CONNECTION_STATUS_REASON_REQUESTED)
899         /* An error occured */
900         return GTK_STOCK_DIALOG_ERROR;
901
902       presence = TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
903     }
904   else if (status == TP_CONNECTION_STATUS_CONNECTING)
905     {
906       /* Account is connecting. Display a blinking account alternating between
907        * the offline icon and the requested presence. */
908       if (priv->connecting_show)
909         presence = tp_account_get_requested_presence (account, NULL, NULL);
910       else
911         presence = TP_CONNECTION_PRESENCE_TYPE_OFFLINE;
912     }
913   else
914     {
915       /* status == TP_CONNECTION_STATUS_CONNECTED */
916       presence = tp_account_get_current_presence (account, NULL, NULL);
917
918       /* If presence is Unset (CM doesn't implement SimplePresence),
919        * display the 'available' icon.
920        * We also check Offline because of this MC5 bug: fd.o #26060 */
921       if (presence == TP_CONNECTION_PRESENCE_TYPE_OFFLINE ||
922           presence == TP_CONNECTION_PRESENCE_TYPE_UNSET)
923         presence = TP_CONNECTION_PRESENCE_TYPE_AVAILABLE;
924     }
925
926   return empathy_icon_name_for_presence (presence);
927 }
928
929 static void
930 accounts_dialog_model_status_pixbuf_data_func (GtkTreeViewColumn *tree_column,
931     GtkCellRenderer *cell,
932     GtkTreeModel *model,
933     GtkTreeIter *iter,
934     EmpathyAccountsDialog *dialog)
935 {
936   TpAccount *account;
937
938   gtk_tree_model_get (model, iter, COL_ACCOUNT, &account, -1);
939
940   g_object_set (cell,
941       "icon-name", get_status_icon_for_account (dialog, account),
942       NULL);
943
944   if (account != NULL)
945     g_object_unref (account);
946 }
947
948 static void
949 accounts_dialog_model_protocol_pixbuf_data_func (GtkTreeViewColumn *tree_column,
950     GtkCellRenderer *cell,
951     GtkTreeModel *model,
952     GtkTreeIter *iter,
953     EmpathyAccountsDialog *dialog)
954 {
955   EmpathyAccountSettings  *settings;
956   gchar              *icon_name;
957   GdkPixbuf          *pixbuf;
958   TpConnectionStatus  status;
959
960   gtk_tree_model_get (model, iter,
961       COL_STATUS, &status,
962       COL_ACCOUNT_SETTINGS, &settings,
963       -1);
964
965   icon_name = empathy_account_settings_get_icon_name (settings);
966   pixbuf = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON);
967
968   g_object_set (cell,
969       "visible", TRUE,
970       "pixbuf", pixbuf,
971       NULL);
972
973   g_object_unref (settings);
974
975   if (pixbuf)
976     g_object_unref (pixbuf);
977 }
978
979 static gboolean
980 accounts_dialog_row_changed_foreach (GtkTreeModel *model,
981     GtkTreePath *path,
982     GtkTreeIter *iter,
983     gpointer user_data)
984 {
985   gtk_tree_model_row_changed (model, path, iter);
986
987   return FALSE;
988 }
989
990 static gboolean
991 accounts_dialog_flash_connecting_cb (EmpathyAccountsDialog *dialog)
992 {
993   GtkTreeView  *view;
994   GtkTreeModel *model;
995   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
996
997   priv->connecting_show = !priv->connecting_show;
998
999   view = GTK_TREE_VIEW (priv->treeview);
1000   model = gtk_tree_view_get_model (view);
1001
1002   gtk_tree_model_foreach (model, accounts_dialog_row_changed_foreach, NULL);
1003
1004   return TRUE;
1005 }
1006
1007 static void
1008 accounts_dialog_name_edited_cb (GtkCellRendererText *renderer,
1009     gchar *path,
1010     gchar *new_text,
1011     EmpathyAccountsDialog *dialog)
1012 {
1013   EmpathyAccountSettings    *settings;
1014   GtkTreeModel *model;
1015   GtkTreePath  *treepath;
1016   GtkTreeIter   iter;
1017   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1018   gboolean connecting;
1019
1020   empathy_account_manager_get_accounts_connected (&connecting);
1021
1022   if (connecting)
1023     {
1024       priv->connecting_id = g_timeout_add (FLASH_TIMEOUT,
1025           (GSourceFunc) accounts_dialog_flash_connecting_cb,
1026           dialog);
1027     }
1028
1029   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1030   treepath = gtk_tree_path_new_from_string (path);
1031   gtk_tree_model_get_iter (model, &iter, treepath);
1032   gtk_tree_model_get (model, &iter,
1033       COL_ACCOUNT_SETTINGS, &settings,
1034       -1);
1035   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1036       COL_NAME, new_text,
1037       -1);
1038   gtk_tree_path_free (treepath);
1039
1040   empathy_account_settings_set_display_name_async (settings, new_text,
1041       NULL, NULL);
1042   g_object_set (settings, "display-name-overridden", TRUE, NULL);
1043   g_object_unref (settings);
1044 }
1045
1046 static void
1047 accounts_dialog_delete_account_response_cb (GtkDialog *message_dialog,
1048   gint response_id,
1049   gpointer user_data)
1050 {
1051   TpAccount *account;
1052   GtkTreeModel *model;
1053   GtkTreeIter iter;
1054   GtkTreeSelection *selection;
1055   EmpathyAccountsDialog *account_dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1056   EmpathyAccountsDialogPriv *priv = GET_PRIV (account_dialog);
1057
1058   if (response_id == GTK_RESPONSE_YES)
1059     {
1060       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1061
1062       if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1063         return;
1064
1065       gtk_tree_model_get (model, &iter, COL_ACCOUNT, &account, -1);
1066
1067       if (account != NULL)
1068         {
1069           tp_account_remove_async (account, NULL, NULL);
1070           g_object_unref (account);
1071           account = NULL;
1072         }
1073
1074       /* No need to call accounts_dialog_model_selection_changed while
1075        * removing as we are going to call accounts_dialog_model_select_first
1076        * right after which will update the selection. */
1077       g_signal_handlers_block_by_func (selection,
1078           accounts_dialog_model_selection_changed, account_dialog);
1079
1080       gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1081
1082       g_signal_handlers_unblock_by_func (selection,
1083           accounts_dialog_model_selection_changed, account_dialog);
1084
1085       accounts_dialog_model_select_first (account_dialog);
1086     }
1087
1088   gtk_widget_destroy (GTK_WIDGET (message_dialog));
1089 }
1090
1091 static void
1092 accounts_dialog_remove_account_iter (EmpathyAccountsDialog *dialog,
1093     GtkTreeIter *iter)
1094 {
1095   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1096   TpAccount *account;
1097   GtkTreeModel *model;
1098   gchar *question_dialog_primary_text;
1099
1100   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1101
1102   gtk_tree_model_get (model, iter, COL_ACCOUNT, &account, -1);
1103
1104   if (account == NULL || !tp_account_is_valid (account))
1105     {
1106       if (account != NULL)
1107         g_object_unref (account);
1108       gtk_list_store_remove (GTK_LIST_STORE (model), iter);
1109       accounts_dialog_model_select_first (dialog);
1110       return;
1111     }
1112
1113   question_dialog_primary_text = g_strdup_printf (
1114       _("Do you want to remove %s from your computer?"),
1115       tp_account_get_display_name (account));
1116
1117   accounts_dialog_show_question_dialog (dialog, question_dialog_primary_text,
1118       _("This will not remove your account on the server."),
1119       G_CALLBACK (accounts_dialog_delete_account_response_cb),
1120       dialog,
1121       GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1122       GTK_STOCK_REMOVE, GTK_RESPONSE_YES, NULL);
1123
1124   g_free (question_dialog_primary_text);
1125   g_object_unref (account);
1126 }
1127
1128 static void
1129 accounts_dialog_button_remove_clicked_cb (GtkWidget *button,
1130     EmpathyAccountsDialog *dialog)
1131 {
1132   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1133   GtkTreeView  *view;
1134   GtkTreeModel *model;
1135   GtkTreeSelection *selection;
1136   GtkTreeIter iter;
1137
1138   view = GTK_TREE_VIEW (priv->treeview);
1139   model = gtk_tree_view_get_model (view);
1140   selection = gtk_tree_view_get_selection (view);
1141   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
1142       return;
1143
1144   accounts_dialog_remove_account_iter (dialog, &iter);
1145 }
1146
1147 #ifdef HAVE_MEEGO
1148 static void
1149 accounts_dialog_view_delete_activated_cb (EmpathyCellRendererActivatable *cell,
1150     const gchar *path_string,
1151     EmpathyAccountsDialog *dialog)
1152 {
1153   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1154   GtkTreeModel *model;
1155   GtkTreeIter iter;
1156
1157   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1158   if (!gtk_tree_model_get_iter_from_string (model, &iter, path_string))
1159     return;
1160
1161   accounts_dialog_remove_account_iter (dialog, &iter);
1162 }
1163 #endif /* HAVE_MEEGO */
1164
1165 static void
1166 accounts_dialog_model_add_columns (EmpathyAccountsDialog *dialog)
1167 {
1168   GtkTreeView       *view;
1169   GtkTreeViewColumn *column;
1170   GtkCellRenderer   *cell;
1171   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1172
1173   view = GTK_TREE_VIEW (priv->treeview);
1174   gtk_tree_view_set_headers_visible (view, FALSE);
1175
1176   /* Account column */
1177   column = gtk_tree_view_column_new ();
1178   gtk_tree_view_column_set_expand (column, TRUE);
1179   gtk_tree_view_append_column (view, column);
1180
1181   /* Status icon renderer */
1182   cell = gtk_cell_renderer_pixbuf_new ();
1183   gtk_tree_view_column_pack_start (column, cell, FALSE);
1184   gtk_tree_view_column_set_cell_data_func (column, cell,
1185       (GtkTreeCellDataFunc)
1186       accounts_dialog_model_status_pixbuf_data_func,
1187       dialog,
1188       NULL);
1189
1190   /* Protocol icon renderer */
1191   cell = gtk_cell_renderer_pixbuf_new ();
1192   gtk_tree_view_column_pack_start (column, cell, FALSE);
1193   gtk_tree_view_column_set_cell_data_func (column, cell,
1194       (GtkTreeCellDataFunc)
1195       accounts_dialog_model_protocol_pixbuf_data_func,
1196       dialog,
1197       NULL);
1198
1199   /* Name renderer */
1200   cell = gtk_cell_renderer_text_new ();
1201   g_object_set (cell,
1202       "ellipsize", PANGO_ELLIPSIZE_END,
1203       "width-chars", 25,
1204       "editable", TRUE,
1205       NULL);
1206   gtk_tree_view_column_pack_start (column, cell, TRUE);
1207   gtk_tree_view_column_add_attribute (column, cell, "text", COL_NAME);
1208   g_signal_connect (cell, "edited",
1209       G_CALLBACK (accounts_dialog_name_edited_cb),
1210       dialog);
1211   g_signal_connect (cell, "editing-started",
1212       G_CALLBACK (accounts_dialog_name_editing_started_cb),
1213       dialog);
1214   g_object_set (cell, "ypad", 4, NULL);
1215
1216 #ifdef HAVE_MEEGO
1217   /* Delete column */
1218   cell = empathy_cell_renderer_activatable_new ();
1219   gtk_tree_view_column_pack_start (column, cell, FALSE);
1220   g_object_set (cell,
1221         "icon-name", GTK_STOCK_DELETE,
1222         "show-on-select", TRUE,
1223         NULL);
1224
1225   g_signal_connect (cell, "path-activated",
1226       G_CALLBACK (accounts_dialog_view_delete_activated_cb),
1227       dialog);
1228 #endif /* HAVE_MEEGO */
1229 }
1230
1231 static EmpathyAccountSettings *
1232 accounts_dialog_model_get_selected_settings (EmpathyAccountsDialog *dialog)
1233 {
1234   GtkTreeView      *view;
1235   GtkTreeModel     *model;
1236   GtkTreeSelection *selection;
1237   GtkTreeIter       iter;
1238   EmpathyAccountSettings   *settings;
1239   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1240
1241   view = GTK_TREE_VIEW (priv->treeview);
1242   selection = gtk_tree_view_get_selection (view);
1243
1244   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1245     return NULL;
1246
1247   gtk_tree_model_get (model, &iter,
1248       COL_ACCOUNT_SETTINGS, &settings, -1);
1249
1250   return settings;
1251 }
1252
1253 static void
1254 accounts_dialog_model_selection_changed (GtkTreeSelection *selection,
1255     EmpathyAccountsDialog *dialog)
1256 {
1257   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1258   EmpathyAccountSettings *settings;
1259   GtkTreeModel *model;
1260   GtkTreeIter   iter;
1261   gboolean      is_selection;
1262   gboolean creating = FALSE;
1263
1264   is_selection = gtk_tree_selection_get_selected (selection, &model, &iter);
1265
1266   settings = accounts_dialog_model_get_selected_settings (dialog);
1267   accounts_dialog_update_settings (dialog, settings);
1268
1269   if (settings != NULL)
1270     g_object_unref (settings);
1271
1272   if (priv->setting_widget_object != NULL)
1273     {
1274       g_object_get (priv->setting_widget_object,
1275           "creating-account", &creating, NULL);
1276     }
1277
1278   /* Update remove button sensitivity */
1279   gtk_widget_set_sensitive (priv->button_remove, is_selection && !creating);
1280 }
1281
1282 static void
1283 accounts_dialog_selection_change_response_cb (GtkDialog *message_dialog,
1284   gint response_id,
1285   gpointer *user_data)
1286 {
1287   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1288   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1289
1290   gtk_widget_destroy (GTK_WIDGET (message_dialog));
1291
1292     if (response_id == GTK_RESPONSE_YES && priv->destination_row != NULL)
1293       {
1294         /* The user wants to lose unsaved changes to the currently selected
1295          * account and select another account. We discard the changes and
1296          * select the other account. */
1297         GtkTreePath *path;
1298         GtkTreeSelection *selection;
1299
1300         priv->force_change_row = TRUE;
1301         empathy_account_widget_discard_pending_changes (
1302             priv->setting_widget_object);
1303
1304         path = gtk_tree_row_reference_get_path (priv->destination_row);
1305         selection = gtk_tree_view_get_selection (
1306             GTK_TREE_VIEW (priv->treeview));
1307
1308         if (path != NULL)
1309           {
1310             /* This will trigger a call to
1311              * accounts_dialog_account_selection_change() */
1312             gtk_tree_selection_select_path (selection, path);
1313             gtk_tree_path_free (path);
1314           }
1315
1316         gtk_tree_row_reference_free (priv->destination_row);
1317       }
1318     else
1319       {
1320         priv->force_change_row = FALSE;
1321       }
1322 }
1323
1324 static gboolean
1325 accounts_dialog_account_selection_change (GtkTreeSelection *selection,
1326     GtkTreeModel *model,
1327     GtkTreePath *path,
1328     gboolean path_currently_selected,
1329     gpointer data)
1330 {
1331   TpAccount *account = NULL;
1332   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (data);
1333   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1334   gboolean ret;
1335
1336   if (priv->force_change_row)
1337     {
1338       /* We came back here because the user wants to discard changes to his
1339        * modified account. The changes have already been discarded so we
1340        * just change the selected row. */
1341       priv->force_change_row = FALSE;
1342       return TRUE;
1343     }
1344
1345   if (accounts_dialog_has_pending_change (dialog, &account))
1346     {
1347       /* The currently selected account has some unsaved changes. We ask
1348        * the user if he really wants to lose his changes and select another
1349        * account */
1350       gchar *question_dialog_primary_text = get_dialog_primary_text (account);
1351       priv->destination_row = gtk_tree_row_reference_new (model, path);
1352
1353       accounts_dialog_show_question_dialog (dialog,
1354           question_dialog_primary_text,
1355           _("You are about to select another account, which will discard\n"
1356               "your changes. Are you sure you want to proceed?"),
1357           G_CALLBACK (accounts_dialog_selection_change_response_cb),
1358           dialog,
1359           GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1360           GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
1361
1362       g_free (question_dialog_primary_text);
1363       ret = FALSE;
1364     }
1365   else
1366     {
1367       ret = TRUE;
1368     }
1369
1370   if (account != NULL)
1371     g_object_unref (account);
1372
1373   return ret;
1374 }
1375
1376 static void
1377 accounts_dialog_model_setup (EmpathyAccountsDialog *dialog)
1378 {
1379   GtkListStore     *store;
1380   GtkTreeSelection *selection;
1381   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1382
1383   store = gtk_list_store_new (COL_COUNT,
1384       G_TYPE_STRING,         /* name */
1385       G_TYPE_UINT,           /* status */
1386       TP_TYPE_ACCOUNT,   /* account */
1387       EMPATHY_TYPE_ACCOUNT_SETTINGS); /* settings */
1388
1389   gtk_tree_view_set_model (GTK_TREE_VIEW (priv->treeview),
1390       GTK_TREE_MODEL (store));
1391
1392   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1393   gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
1394   gtk_tree_selection_set_select_function (selection,
1395       accounts_dialog_account_selection_change, dialog, NULL);
1396
1397   g_signal_connect (selection, "changed",
1398       G_CALLBACK (accounts_dialog_model_selection_changed),
1399       dialog);
1400
1401   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
1402       COL_NAME, GTK_SORT_ASCENDING);
1403
1404   accounts_dialog_model_add_columns (dialog);
1405
1406   g_object_unref (store);
1407 }
1408
1409 static gboolean
1410 accounts_dialog_get_settings_iter (EmpathyAccountsDialog *dialog,
1411     EmpathyAccountSettings *settings,
1412     GtkTreeIter *iter)
1413 {
1414   GtkTreeView      *view;
1415   GtkTreeSelection *selection;
1416   GtkTreeModel     *model;
1417   gboolean          ok;
1418   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1419
1420   /* Update the status in the model */
1421   view = GTK_TREE_VIEW (priv->treeview);
1422   selection = gtk_tree_view_get_selection (view);
1423   model = gtk_tree_view_get_model (view);
1424
1425   for (ok = gtk_tree_model_get_iter_first (model, iter);
1426        ok;
1427        ok = gtk_tree_model_iter_next (model, iter))
1428     {
1429       EmpathyAccountSettings *this_settings;
1430       gboolean   equal;
1431
1432       gtk_tree_model_get (model, iter,
1433           COL_ACCOUNT_SETTINGS, &this_settings,
1434           -1);
1435
1436       equal = (this_settings == settings);
1437       g_object_unref (this_settings);
1438
1439       if (equal)
1440         return TRUE;
1441     }
1442
1443   return FALSE;
1444 }
1445
1446 static gboolean
1447 accounts_dialog_get_account_iter (EmpathyAccountsDialog *dialog,
1448     TpAccount *account,
1449     GtkTreeIter *iter)
1450 {
1451   GtkTreeView      *view;
1452   GtkTreeSelection *selection;
1453   GtkTreeModel     *model;
1454   gboolean          ok;
1455   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1456
1457   /* Update the status in the model */
1458   view = GTK_TREE_VIEW (priv->treeview);
1459   selection = gtk_tree_view_get_selection (view);
1460   model = gtk_tree_view_get_model (view);
1461
1462   for (ok = gtk_tree_model_get_iter_first (model, iter);
1463        ok;
1464        ok = gtk_tree_model_iter_next (model, iter))
1465     {
1466       EmpathyAccountSettings *settings;
1467       gboolean   equal;
1468
1469       gtk_tree_model_get (model, iter,
1470           COL_ACCOUNT_SETTINGS, &settings,
1471           -1);
1472
1473       equal = empathy_account_settings_has_account (settings, account);
1474       g_object_unref (settings);
1475
1476       if (equal)
1477         return TRUE;
1478     }
1479
1480   return FALSE;
1481 }
1482
1483 static void
1484 accounts_dialog_model_set_selected (EmpathyAccountsDialog *dialog,
1485     EmpathyAccountSettings *settings)
1486 {
1487   GtkTreeSelection *selection;
1488   GtkTreeIter       iter;
1489   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1490
1491   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1492   if (accounts_dialog_get_settings_iter (dialog, settings, &iter))
1493     gtk_tree_selection_select_iter (selection, &iter);
1494 }
1495 static void
1496 accounts_dialog_add (EmpathyAccountsDialog *dialog,
1497     EmpathyAccountSettings *settings)
1498 {
1499   GtkTreeModel       *model;
1500   GtkTreeIter         iter;
1501   const gchar        *name;
1502   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1503
1504   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1505   name = empathy_account_settings_get_display_name (settings);
1506
1507   gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1508
1509   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1510       COL_NAME, name,
1511       COL_STATUS, TP_CONNECTION_STATUS_DISCONNECTED,
1512       COL_ACCOUNT_SETTINGS, settings,
1513       -1);
1514 }
1515
1516 static void
1517 accounts_dialog_connection_changed_cb (TpAccount *account,
1518     guint old_status,
1519     guint current,
1520     guint reason,
1521     gchar *dbus_error_name,
1522     GHashTable *details,
1523     EmpathyAccountsDialog *dialog)
1524 {
1525   GtkTreeModel *model;
1526   GtkTreeIter   iter;
1527   gboolean      found;
1528   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1529
1530   /* Update the status-infobar in the details view */
1531   accounts_dialog_update_status_infobar (dialog, account);
1532
1533   /* Update the status in the model */
1534   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1535
1536   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1537     {
1538       GtkTreePath *path;
1539
1540       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1541           COL_STATUS, current,
1542           -1);
1543
1544       path = gtk_tree_model_get_path (model, &iter);
1545       gtk_tree_model_row_changed (model, path, &iter);
1546       gtk_tree_path_free (path);
1547     }
1548
1549   empathy_account_manager_get_accounts_connected (&found);
1550
1551   if (!found && priv->connecting_id)
1552     {
1553       g_source_remove (priv->connecting_id);
1554       priv->connecting_id = 0;
1555     }
1556
1557   if (found && !priv->connecting_id)
1558     priv->connecting_id = g_timeout_add (FLASH_TIMEOUT,
1559         (GSourceFunc) accounts_dialog_flash_connecting_cb,
1560         dialog);
1561 }
1562
1563 static void
1564 update_account_in_treeview (EmpathyAccountsDialog *self,
1565     TpAccount *account)
1566 {
1567   EmpathyAccountsDialogPriv *priv = GET_PRIV (self);
1568   GtkTreeIter iter;
1569   GtkTreeModel *model;
1570
1571   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1572   if (accounts_dialog_get_account_iter (self, account, &iter))
1573     {
1574       GtkTreePath *path;
1575
1576       path = gtk_tree_model_get_path (model, &iter);
1577       gtk_tree_model_row_changed (model, path, &iter);
1578       gtk_tree_path_free (path);
1579     }
1580 }
1581
1582 static void
1583 accounts_dialog_presence_changed_cb (TpAccount *account,
1584     guint presence,
1585     gchar *status,
1586     gchar *status_message,
1587     EmpathyAccountsDialog *dialog)
1588 {
1589   /* Update the status-infobar in the details view */
1590   accounts_dialog_update_status_infobar (dialog, account);
1591
1592   update_account_in_treeview (dialog, account);
1593 }
1594
1595 static void
1596 accounts_dialog_account_display_name_changed_cb (TpAccount *account,
1597   GParamSpec *pspec,
1598   gpointer user_data)
1599 {
1600   const gchar *display_name;
1601   GtkTreeIter iter;
1602   GtkTreeModel *model;
1603   EmpathyAccountSettings *settings;
1604   TpAccount *selected_account;
1605   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1606   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1607
1608   display_name = tp_account_get_display_name (account);
1609   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1610   settings = accounts_dialog_model_get_selected_settings (dialog);
1611   if (settings == NULL)
1612     return;
1613
1614   selected_account = empathy_account_settings_get_account (settings);
1615
1616   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1617     {
1618       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1619           COL_NAME, display_name,
1620           -1);
1621     }
1622
1623   if (selected_account == account)
1624     accounts_dialog_update_name_label (dialog, display_name);
1625
1626   g_object_unref (settings);
1627 }
1628
1629 static void
1630 accounts_dialog_add_account (EmpathyAccountsDialog *dialog,
1631     TpAccount *account)
1632 {
1633   EmpathyAccountSettings *settings;
1634   GtkTreeModel       *model;
1635   GtkTreeIter         iter;
1636   TpConnectionStatus  status;
1637   const gchar        *name;
1638   gboolean            enabled;
1639   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1640
1641   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1642   status = tp_account_get_connection_status (account, NULL);
1643   name = tp_account_get_display_name (account);
1644   enabled = tp_account_is_enabled (account);
1645
1646   settings = empathy_account_settings_new_for_account (account);
1647
1648   if (!accounts_dialog_get_account_iter (dialog, account, &iter))
1649     gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1650
1651   gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1652       COL_NAME, name,
1653       COL_STATUS, status,
1654       COL_ACCOUNT, account,
1655       COL_ACCOUNT_SETTINGS, settings,
1656       -1);
1657
1658   accounts_dialog_connection_changed_cb (account,
1659       0,
1660       status,
1661       TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED,
1662       NULL,
1663       NULL,
1664       dialog);
1665
1666   tp_g_signal_connect_object (account, "notify::display-name",
1667       G_CALLBACK (accounts_dialog_account_display_name_changed_cb),
1668       dialog, 0);
1669
1670   tp_g_signal_connect_object (account, "status-changed",
1671       G_CALLBACK (accounts_dialog_connection_changed_cb), dialog, 0);
1672   tp_g_signal_connect_object (account, "presence-changed",
1673       G_CALLBACK (accounts_dialog_presence_changed_cb), dialog, 0);
1674
1675   g_object_unref (settings);
1676 }
1677
1678 static void
1679 account_prepare_cb (GObject *source_object,
1680     GAsyncResult *result,
1681     gpointer user_data)
1682 {
1683   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data);
1684   TpAccount *account = TP_ACCOUNT (source_object);
1685   GError *error = NULL;
1686
1687   if (!tp_account_prepare_finish (account, result, &error))
1688     {
1689       DEBUG ("Failed to prepare account: %s", error->message);
1690       g_error_free (error);
1691       return;
1692     }
1693
1694   accounts_dialog_add_account (dialog, account);
1695 }
1696
1697 static void
1698 accounts_dialog_account_validity_changed_cb (TpAccountManager *manager,
1699     TpAccount *account,
1700     gboolean valid,
1701     EmpathyAccountsDialog *dialog)
1702 {
1703   tp_account_prepare_async (account, NULL, account_prepare_cb, dialog);
1704 }
1705
1706 static void
1707 accounts_dialog_accounts_model_row_inserted_cb (GtkTreeModel *model,
1708     GtkTreePath *path,
1709     GtkTreeIter *iter,
1710     EmpathyAccountsDialog *dialog)
1711 {
1712   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1713
1714   if (priv->setting_widget_object != NULL &&
1715       accounts_dialog_has_valid_accounts (dialog))
1716     {
1717       empathy_account_widget_set_other_accounts_exist (
1718           priv->setting_widget_object, TRUE);
1719     }
1720 }
1721
1722 static void
1723 accounts_dialog_accounts_model_row_deleted_cb (GtkTreeModel *model,
1724     GtkTreePath *path,
1725     EmpathyAccountsDialog *dialog)
1726 {
1727   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1728
1729   if (priv->setting_widget_object != NULL &&
1730       !accounts_dialog_has_valid_accounts (dialog))
1731     {
1732       empathy_account_widget_set_other_accounts_exist (
1733           priv->setting_widget_object, FALSE);
1734     }
1735 }
1736
1737 static void
1738 accounts_dialog_account_removed_cb (TpAccountManager *manager,
1739     TpAccount *account,
1740     EmpathyAccountsDialog *dialog)
1741 {
1742   GtkTreeIter iter;
1743   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1744
1745   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1746     {
1747       gtk_list_store_remove (GTK_LIST_STORE (
1748           gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview))), &iter);
1749     }
1750 }
1751
1752 static void
1753 enable_or_disable_account (EmpathyAccountsDialog *dialog,
1754     TpAccount *account,
1755     gboolean enabled)
1756 {
1757   GtkTreeModel *model;
1758   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1759
1760   /* Update the status in the model */
1761   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
1762
1763   /* Update the status-infobar in the details view */
1764   accounts_dialog_update_status_infobar (dialog, account);
1765
1766   DEBUG ("Account %s is now %s",
1767       tp_account_get_display_name (account),
1768       enabled ? "enabled" : "disabled");
1769 }
1770
1771 static void
1772 accounts_dialog_account_disabled_cb (TpAccountManager *manager,
1773     TpAccount *account,
1774     EmpathyAccountsDialog *dialog)
1775 {
1776   enable_or_disable_account (dialog, account, FALSE);
1777   update_account_in_treeview (dialog, account);
1778 }
1779
1780 static void
1781 accounts_dialog_account_enabled_cb (TpAccountManager *manager,
1782     TpAccount *account,
1783     EmpathyAccountsDialog *dialog)
1784 {
1785   enable_or_disable_account (dialog, account, TRUE);
1786 }
1787
1788 static void
1789 accounts_dialog_button_import_clicked_cb (GtkWidget *button,
1790     EmpathyAccountsDialog *dialog)
1791 {
1792   GtkWidget *import_dialog;
1793
1794   import_dialog = empathy_import_dialog_new (GTK_WINDOW (dialog),
1795       FALSE);
1796   gtk_widget_show (import_dialog);
1797 }
1798
1799 static void
1800 accounts_dialog_close_response_cb (GtkDialog *message_dialog,
1801   gint response_id,
1802   gpointer user_data)
1803 {
1804   GtkWidget *account_dialog = GTK_WIDGET (user_data);
1805
1806   gtk_widget_destroy (GTK_WIDGET (message_dialog));
1807
1808   if (response_id == GTK_RESPONSE_YES)
1809     gtk_widget_destroy (account_dialog);
1810 }
1811
1812 static gboolean
1813 accounts_dialog_delete_event_cb (GtkWidget *widget,
1814     GdkEvent *event,
1815     EmpathyAccountsDialog *dialog)
1816 {
1817   /* we maunally handle responses to delete events */
1818   return TRUE;
1819 }
1820
1821 static void
1822 accounts_dialog_set_selected_account (EmpathyAccountsDialog *dialog,
1823     TpAccount *account)
1824 {
1825   GtkTreeSelection *selection;
1826   GtkTreeIter       iter;
1827   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1828
1829   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
1830   if (accounts_dialog_get_account_iter (dialog, account, &iter))
1831     gtk_tree_selection_select_iter (selection, &iter);
1832 }
1833
1834 static void
1835 accounts_dialog_cms_prepare_cb (GObject *source,
1836     GAsyncResult *result,
1837     gpointer user_data)
1838 {
1839   EmpathyConnectionManagers *cms = EMPATHY_CONNECTION_MANAGERS (source);
1840   EmpathyAccountsDialog *dialog = user_data;
1841   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1842
1843   if (!empathy_connection_managers_prepare_finish (cms, result, NULL))
1844     return;
1845
1846   accounts_dialog_update_settings (dialog, NULL);
1847
1848   if (priv->initial_selection != NULL)
1849     {
1850       accounts_dialog_set_selected_account (dialog, priv->initial_selection);
1851       g_object_unref (priv->initial_selection);
1852       priv->initial_selection = NULL;
1853     }
1854 }
1855
1856 static void
1857 accounts_dialog_accounts_setup (EmpathyAccountsDialog *dialog)
1858 {
1859   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
1860   GList *accounts, *l;
1861
1862   g_signal_connect (priv->account_manager, "account-validity-changed",
1863       G_CALLBACK (accounts_dialog_account_validity_changed_cb),
1864       dialog);
1865   g_signal_connect (priv->account_manager, "account-removed",
1866       G_CALLBACK (accounts_dialog_account_removed_cb),
1867       dialog);
1868   g_signal_connect (priv->account_manager, "account-enabled",
1869       G_CALLBACK (accounts_dialog_account_enabled_cb),
1870       dialog);
1871   g_signal_connect (priv->account_manager, "account-disabled",
1872       G_CALLBACK (accounts_dialog_account_disabled_cb),
1873       dialog);
1874
1875   /* Add existing accounts */
1876   accounts = tp_account_manager_get_valid_accounts (priv->account_manager);
1877   for (l = accounts; l; l = l->next)
1878     {
1879       accounts_dialog_add_account (dialog, l->data);
1880     }
1881   g_list_free (accounts);
1882
1883   priv->cms = empathy_connection_managers_dup_singleton ();
1884
1885   empathy_connection_managers_prepare_async (priv->cms,
1886       accounts_dialog_cms_prepare_cb, dialog);
1887
1888   accounts_dialog_model_select_first (dialog);
1889 }
1890
1891 static void
1892 accounts_dialog_manager_ready_cb (GObject *source_object,
1893     GAsyncResult *result,
1894     gpointer user_data)
1895 {
1896   TpAccountManager *manager = TP_ACCOUNT_MANAGER (source_object);
1897   GError *error = NULL;
1898
1899   if (!tp_account_manager_prepare_finish (manager, result, &error))
1900     {
1901       DEBUG ("Failed to prepare account manager: %s", error->message);
1902       g_error_free (error);
1903       return;
1904     }
1905
1906   accounts_dialog_accounts_setup (user_data);
1907 }
1908
1909 static void
1910 dialog_response_cb (GtkWidget *widget,
1911     gint response_id,
1912     gpointer user_data)
1913 {
1914   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (widget);
1915
1916   if (response_id == GTK_RESPONSE_HELP)
1917     {
1918       empathy_url_show (widget, "ghelp:empathy?accounts-window");
1919     }
1920   else if (response_id == GTK_RESPONSE_CLOSE ||
1921       response_id == GTK_RESPONSE_DELETE_EVENT)
1922     {
1923       TpAccount *account = NULL;
1924
1925       if (accounts_dialog_has_pending_change (dialog, &account))
1926         {
1927           gchar *question_dialog_primary_text = get_dialog_primary_text (
1928               account);
1929
1930           accounts_dialog_show_question_dialog (dialog,
1931               question_dialog_primary_text,
1932               _("You are about to close the window, which will discard\n"
1933                   "your changes. Are you sure you want to proceed?"),
1934               G_CALLBACK (accounts_dialog_close_response_cb),
1935               widget,
1936               GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1937               GTK_STOCK_DISCARD, GTK_RESPONSE_YES, NULL);
1938
1939           g_free (question_dialog_primary_text);
1940         }
1941       else
1942         {
1943           gtk_widget_destroy (widget);
1944         }
1945
1946       if (account != NULL)
1947         g_object_unref (account);
1948     }
1949 }
1950
1951 static void
1952 accounts_dialog_build_ui (EmpathyAccountsDialog *dialog)
1953 {
1954   GtkWidget *top_hbox;
1955   GtkBuilder                   *gui;
1956   gchar                        *filename;
1957   EmpathyAccountsDialogPriv    *priv = GET_PRIV (dialog);
1958   GtkWidget                    *content_area;
1959   GtkWidget *action_area, *vbox, *hbox, *align;
1960
1961   filename = empathy_file_lookup ("empathy-accounts-dialog.ui", "src");
1962
1963   gui = empathy_builder_get_file (filename,
1964       "accounts_dialog_hbox", &top_hbox,
1965       "vbox_details", &priv->vbox_details,
1966       "frame_no_protocol", &priv->frame_no_protocol,
1967       "alignment_settings", &priv->alignment_settings,
1968       "alignment_infobar", &priv->alignment_infobar,
1969       "treeview", &priv->treeview,
1970       "button_add", &priv->button_add,
1971       "button_remove", &priv->button_remove,
1972       "button_import", &priv->button_import,
1973       "hbox_protocol", &priv->hbox_protocol,
1974       NULL);
1975   g_free (filename);
1976
1977   gtk_widget_set_no_show_all (priv->frame_no_protocol, TRUE);
1978
1979   empathy_builder_connect (gui, dialog,
1980       "button_add", "clicked", accounts_dialog_button_add_clicked_cb,
1981       "button_remove", "clicked", accounts_dialog_button_remove_clicked_cb,
1982       "button_import", "clicked", accounts_dialog_button_import_clicked_cb,
1983       NULL);
1984
1985   content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
1986
1987   gtk_container_add (GTK_CONTAINER (content_area), top_hbox);
1988
1989   g_object_unref (gui);
1990
1991   action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
1992
1993 #ifdef HAVE_MEEGO
1994   gtk_widget_hide (action_area);
1995   gtk_widget_hide (priv->button_remove);
1996 #endif /* HAVE_MEEGO */
1997
1998   /* Remove button is unsensitive until we have a selected account */
1999   gtk_widget_set_sensitive (priv->button_remove, FALSE);
2000
2001   priv->combobox_protocol = empathy_protocol_chooser_new ();
2002   gtk_box_pack_start (GTK_BOX (priv->hbox_protocol), priv->combobox_protocol,
2003       TRUE, TRUE, 0);
2004   g_signal_connect (priv->combobox_protocol, "changed",
2005       G_CALLBACK (accounts_dialog_protocol_changed_cb),
2006       dialog);
2007
2008   if (priv->parent_window)
2009     gtk_window_set_transient_for (GTK_WINDOW (dialog),
2010         priv->parent_window);
2011
2012   priv->infobar = gtk_info_bar_new ();
2013   gtk_container_add (GTK_CONTAINER (priv->alignment_infobar),
2014       priv->infobar);
2015   gtk_widget_show (priv->infobar);
2016
2017   content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (priv->infobar));
2018
2019   priv->image_type = gtk_image_new_from_stock (GTK_STOCK_CUT,
2020       GTK_ICON_SIZE_DIALOG);
2021   gtk_misc_set_alignment (GTK_MISC (priv->image_type), 0.0, 0.5);
2022   gtk_box_pack_start (GTK_BOX (content_area), priv->image_type, FALSE, FALSE, 0);
2023   gtk_widget_show (priv->image_type);
2024
2025   vbox = gtk_vbox_new (FALSE, 6);
2026   gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0);
2027   gtk_widget_show (vbox);
2028
2029   /* first row */
2030   align = gtk_alignment_new (0.5, 0.0, 0.0, 0.0);
2031   gtk_widget_show (align);
2032
2033   priv->label_name = gtk_label_new (NULL);
2034   gtk_container_add (GTK_CONTAINER (align), priv->label_name);
2035   gtk_widget_show (priv->label_name);
2036
2037   gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0);
2038
2039   /* second row */
2040   align = gtk_alignment_new (0.5, 0.0, 0.0, 0.0);
2041   gtk_widget_show (align);
2042   hbox = gtk_hbox_new (FALSE, 6);
2043   gtk_widget_show (hbox);
2044   gtk_container_add (GTK_CONTAINER (align), hbox);
2045
2046   gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0);
2047
2048   /* set up spinner */
2049   priv->throbber = gtk_spinner_new ();
2050
2051   priv->image_status = gtk_image_new_from_icon_name (
2052             empathy_icon_name_for_presence (
2053             TP_CONNECTION_PRESENCE_TYPE_OFFLINE), GTK_ICON_SIZE_SMALL_TOOLBAR);
2054
2055   priv->label_status = gtk_label_new (NULL);
2056   gtk_label_set_line_wrap (GTK_LABEL (priv->label_status), TRUE);
2057   gtk_widget_show (priv->label_status);
2058
2059   gtk_box_pack_start (GTK_BOX (hbox), priv->throbber, FALSE, FALSE, 0);
2060   gtk_box_pack_start (GTK_BOX (hbox), priv->image_status, FALSE, FALSE, 3);
2061   gtk_box_pack_start (GTK_BOX (hbox), priv->label_status, TRUE, TRUE, 0);
2062
2063   /* Tweak the dialog */
2064   gtk_window_set_title (GTK_WINDOW (dialog), _("Messaging and VoIP Accounts"));
2065   gtk_window_set_role (GTK_WINDOW (dialog), "accounts");
2066
2067   gtk_window_set_default_size (GTK_WINDOW (dialog), 640, 450);
2068
2069   gtk_window_set_type_hint (GTK_WINDOW (dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
2070
2071   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
2072
2073   /* add dialog buttons */
2074   gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), GTK_BUTTONBOX_END);
2075
2076   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
2077       GTK_STOCK_HELP, GTK_RESPONSE_HELP,
2078       GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
2079       NULL);
2080
2081   g_signal_connect (dialog, "response",
2082       G_CALLBACK (dialog_response_cb), dialog);
2083
2084   g_signal_connect (dialog, "delete-event",
2085       G_CALLBACK (accounts_dialog_delete_event_cb), dialog);
2086 }
2087
2088 static void
2089 do_dispose (GObject *obj)
2090 {
2091   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (obj);
2092   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2093   GtkTreeModel *model;
2094
2095   if (priv->dispose_has_run)
2096     return;
2097
2098   priv->dispose_has_run = TRUE;
2099
2100   /* Disconnect signals */
2101   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
2102   g_signal_handlers_disconnect_by_func (model,
2103       accounts_dialog_accounts_model_row_inserted_cb, dialog);
2104   g_signal_handlers_disconnect_by_func (model,
2105       accounts_dialog_accounts_model_row_deleted_cb, dialog);
2106
2107   g_signal_handlers_disconnect_by_func (priv->account_manager,
2108       accounts_dialog_account_validity_changed_cb,
2109       dialog);
2110   g_signal_handlers_disconnect_by_func (priv->account_manager,
2111       accounts_dialog_account_removed_cb,
2112       dialog);
2113   g_signal_handlers_disconnect_by_func (priv->account_manager,
2114       accounts_dialog_account_enabled_cb,
2115       dialog);
2116   g_signal_handlers_disconnect_by_func (priv->account_manager,
2117       accounts_dialog_account_disabled_cb,
2118       dialog);
2119   g_signal_handlers_disconnect_by_func (priv->account_manager,
2120       accounts_dialog_manager_ready_cb,
2121       dialog);
2122
2123   if (priv->connecting_id)
2124     g_source_remove (priv->connecting_id);
2125
2126   if (priv->account_manager != NULL)
2127     {
2128       g_object_unref (priv->account_manager);
2129       priv->account_manager = NULL;
2130     }
2131
2132   if (priv->cms != NULL)
2133     {
2134       g_object_unref (priv->cms);
2135       priv->cms = NULL;
2136     }
2137
2138   if (priv->initial_selection != NULL)
2139     g_object_unref (priv->initial_selection);
2140   priv->initial_selection = NULL;
2141
2142   G_OBJECT_CLASS (empathy_accounts_dialog_parent_class)->dispose (obj);
2143 }
2144
2145 static void
2146 do_get_property (GObject *object,
2147     guint property_id,
2148     GValue *value,
2149     GParamSpec *pspec)
2150 {
2151   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
2152
2153   switch (property_id)
2154     {
2155     case PROP_PARENT:
2156       g_value_set_object (value, priv->parent_window);
2157       break;
2158     default:
2159       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2160     }
2161 }
2162
2163 static void
2164 do_set_property (GObject *object,
2165     guint property_id,
2166     const GValue *value,
2167     GParamSpec *pspec)
2168 {
2169   EmpathyAccountsDialogPriv *priv = GET_PRIV (object);
2170
2171   switch (property_id)
2172     {
2173     case PROP_PARENT:
2174       priv->parent_window = g_value_get_object (value);
2175       break;
2176     default:
2177       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2178     }
2179 }
2180
2181 static void
2182 do_constructed (GObject *object)
2183 {
2184   EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (object);
2185   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2186   gboolean import_asked;
2187   GtkTreeModel *model;
2188
2189   accounts_dialog_build_ui (dialog);
2190   accounts_dialog_model_setup (dialog);
2191
2192   model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));
2193   g_signal_connect (model, "row-inserted",
2194       (GCallback) accounts_dialog_accounts_model_row_inserted_cb, dialog);
2195   g_signal_connect (model, "row-deleted",
2196       (GCallback) accounts_dialog_accounts_model_row_deleted_cb, dialog);
2197
2198   /* Set up signalling */
2199   priv->account_manager = tp_account_manager_dup ();
2200
2201   tp_account_manager_prepare_async (priv->account_manager, NULL,
2202       accounts_dialog_manager_ready_cb, dialog);
2203
2204   empathy_conf_get_bool (empathy_conf_get (),
2205       EMPATHY_PREFS_IMPORT_ASKED, &import_asked);
2206
2207   if (empathy_import_accounts_to_import ())
2208     {
2209       gtk_widget_show (priv->button_import);
2210
2211       if (!import_asked)
2212         {
2213           GtkWidget *import_dialog;
2214
2215           empathy_conf_set_bool (empathy_conf_get (),
2216               EMPATHY_PREFS_IMPORT_ASKED, TRUE);
2217           import_dialog = empathy_import_dialog_new (GTK_WINDOW (dialog),
2218               FALSE);
2219           gtk_widget_show (import_dialog);
2220         }
2221     }
2222 }
2223
2224 static void
2225 empathy_accounts_dialog_class_init (EmpathyAccountsDialogClass *klass)
2226 {
2227   GObjectClass *oclass = G_OBJECT_CLASS (klass);
2228   GParamSpec *param_spec;
2229
2230   oclass->dispose = do_dispose;
2231   oclass->constructed = do_constructed;
2232   oclass->set_property = do_set_property;
2233   oclass->get_property = do_get_property;
2234
2235   param_spec = g_param_spec_object ("parent",
2236       "parent", "The parent window",
2237       GTK_TYPE_WINDOW,
2238       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2239   g_object_class_install_property (oclass, PROP_PARENT, param_spec);
2240
2241   g_type_class_add_private (klass, sizeof (EmpathyAccountsDialogPriv));
2242 }
2243
2244 static void
2245 empathy_accounts_dialog_init (EmpathyAccountsDialog *dialog)
2246 {
2247   EmpathyAccountsDialogPriv *priv;
2248
2249   priv = G_TYPE_INSTANCE_GET_PRIVATE ((dialog),
2250       EMPATHY_TYPE_ACCOUNTS_DIALOG,
2251       EmpathyAccountsDialogPriv);
2252   dialog->priv = priv;
2253 }
2254
2255 /* public methods */
2256
2257 GtkWidget *
2258 empathy_accounts_dialog_show (GtkWindow *parent,
2259     TpAccount *selected_account)
2260 {
2261   EmpathyAccountsDialog *dialog;
2262   EmpathyAccountsDialogPriv *priv;
2263
2264   dialog = g_object_new (EMPATHY_TYPE_ACCOUNTS_DIALOG,
2265       "parent", parent, NULL);
2266
2267   priv = GET_PRIV (dialog);
2268
2269   if (selected_account)
2270     {
2271       if (priv->cms != NULL && empathy_connection_managers_is_ready (priv->cms))
2272         accounts_dialog_set_selected_account (dialog, selected_account);
2273       else
2274         /* save the selection to set it later when the cms
2275          * becomes ready.
2276          */
2277         priv->initial_selection = g_object_ref (selected_account);
2278     }
2279
2280   gtk_window_present (GTK_WINDOW (dialog));
2281
2282   return GTK_WIDGET (dialog);
2283 }
2284
2285 void
2286 empathy_accounts_dialog_show_application (GdkScreen *screen,
2287     TpAccount *selected_account,
2288     gboolean if_needed,
2289     gboolean hidden)
2290 {
2291   GError *error = NULL;
2292   gchar *argv[4] = { NULL, };
2293   gint i = 0;
2294   gchar *account_option = NULL;
2295   gchar *path;
2296
2297   g_return_if_fail (GDK_IS_SCREEN (screen));
2298   g_return_if_fail (!selected_account || TP_IS_ACCOUNT (selected_account));
2299
2300   /* Try to run from source directory if possible */
2301   path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "src",
2302       "empathy-accounts", NULL);
2303
2304   if (!g_file_test (path, G_FILE_TEST_EXISTS))
2305     {
2306       g_free (path);
2307       path = g_build_filename (BIN_DIR, "empathy-accounts", NULL);
2308     }
2309
2310   argv[i++] = path;
2311
2312   if (selected_account != NULL)
2313     {
2314       const gchar *account_path;
2315
2316       account_path = tp_proxy_get_object_path (TP_PROXY (selected_account));
2317       account_option = g_strdup_printf ("--select-account=%s",
2318           &account_path[strlen (TP_ACCOUNT_OBJECT_PATH_BASE)]);
2319
2320       argv[i++] = account_option;
2321     }
2322
2323   if (if_needed)
2324     argv[i++] = "--if-needed";
2325
2326   if (hidden)
2327     argv[i++] = "--hidden";
2328
2329   DEBUG ("Launching empathy-accounts (if_needed: %d, hidden: %d, account: %s)",
2330     if_needed, hidden,
2331     selected_account == NULL ? "<none selected>" :
2332       tp_proxy_get_object_path (TP_PROXY (selected_account)));
2333
2334   gdk_spawn_on_screen (screen, NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
2335       NULL, NULL, NULL, &error);
2336   if (error != NULL)
2337     {
2338       g_warning ("Failed to open accounts dialog: %s", error->message);
2339       g_error_free (error);
2340     }
2341
2342   g_free (account_option);
2343   g_free (path);
2344 }
2345
2346 gboolean
2347 empathy_accounts_dialog_is_creating (EmpathyAccountsDialog *dialog)
2348 {
2349   EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog);
2350   gboolean result = FALSE;
2351
2352   if (priv->setting_widget_object == NULL)
2353     goto out;
2354
2355   g_object_get (priv->setting_widget_object,
2356       "creating-account", &result, NULL);
2357
2358 out:
2359   return result;
2360 }