]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-account-widget.c
account-widget: add Remember Password widgets for all the other protocols
[empathy.git] / libempathy-gtk / empathy-account-widget.c
1 /*
2  * Copyright (C) 2006-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: Xavier Claessens <xclaesse@gmail.com>
21  *          Martyn Russell <martyn@imendio.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
30 #include <gtk/gtk.h>
31 #include <glib/gi18n-lib.h>
32
33 #ifdef HAVE_MEEGO
34 #include <mx-gtk/mx-gtk.h>
35 #endif /* HAVE_MEEGO */
36 #include <gio/gdesktopappinfo.h>
37
38 #include <libempathy/empathy-utils.h>
39
40 #include <telepathy-glib/account.h>
41 #include <telepathy-glib/account-manager.h>
42 #include <telepathy-glib/connection-manager.h>
43 #include <telepathy-glib/util.h>
44 #include <dbus/dbus-protocol.h>
45
46 #include "empathy-account-widget.h"
47 #include "empathy-account-widget-private.h"
48 #include "empathy-account-widget-sip.h"
49 #include "empathy-account-widget-irc.h"
50 #include "empathy-ui-utils.h"
51
52 #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
53 #include <libempathy/empathy-debug.h>
54
55 G_DEFINE_TYPE (EmpathyAccountWidget, empathy_account_widget, G_TYPE_OBJECT)
56
57 typedef enum
58 {
59   NO_SERVICE = 0,
60   GTALK_SERVICE,
61   FACEBOOK_SERVICE,
62   N_SERVICES
63 } Service;
64
65 typedef struct
66 {
67   const gchar *label_username_example;
68   gboolean show_advanced;
69 } ServiceInfo;
70
71 static ServiceInfo services_infos[N_SERVICES] = {
72     { "label_username_example", TRUE },
73     { "label_username_g_example", TRUE },
74     { "label_username_f_example", FALSE },
75 };
76
77 typedef struct {
78   EmpathyAccountSettings *settings;
79
80   GtkWidget *table_common_settings;
81   GtkWidget *apply_button;
82   GtkWidget *cancel_button;
83   GtkWidget *entry_password;
84   GtkWidget *spinbutton_port;
85   GtkWidget *enabled_checkbox;
86   GtkWidget *radiobutton_reuse;
87
88   gboolean simple;
89
90   gboolean contains_pending_changes;
91
92   /* An EmpathyAccountWidget can be used to either create an account or
93    * modify it. When we are creating an account, this member is set to TRUE */
94   gboolean creating_account;
95
96   /* whether there are any other real accounts. Necessary so we know whether
97    * it's safe to dismiss this widget in some cases (eg, whether the Cancel
98    * button should be sensitive) */
99   gboolean other_accounts_exist;
100
101   /* if TRUE, the GTK+ destroy signal has been fired and so the widgets
102    * embedded in this account widget can't be used any more
103    * workaround because some async callbacks can be called after the
104    * widget has been destroyed */
105   gboolean destroyed;
106
107   TpAccountManager *account_manager;
108
109   GtkWidget *param_account_widget;
110   GtkWidget *param_password_widget;
111
112   gboolean automatic_change;
113   GtkWidget *remember_password_widget;
114
115   /* Used only for IRC accounts */
116   EmpathyIrcNetworkChooser *irc_network_chooser;
117
118   /* Used for 'special' XMPP account having a service associated ensuring that
119    * JIDs have a specific suffix; such as Facebook for example */
120   gchar *jid_suffix;
121
122   gboolean dispose_run;
123 } EmpathyAccountWidgetPriv;
124
125 enum {
126   PROP_PROTOCOL = 1,
127   PROP_SETTINGS,
128   PROP_SIMPLE,
129   PROP_CREATING_ACCOUNT,
130   PROP_OTHER_ACCOUNTS_EXIST,
131 };
132
133 enum {
134   HANDLE_APPLY,
135   ACCOUNT_CREATED,
136   CANCELLED,
137   LAST_SIGNAL
138 };
139
140 static void account_widget_apply_and_log_in (EmpathyAccountWidget *);
141
142 enum {
143   RESPONSE_LAUNCH
144 };
145
146 static guint signals[LAST_SIGNAL] = { 0 };
147
148 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccountWidget)
149 #define CHANGED_TIMEOUT 300
150
151 #define DIGIT             "0-9"
152 #define DIGITS            "(["DIGIT"]+)"
153 #define ALPHA             "a-zA-Z"
154 #define ALPHAS            "(["ALPHA"]+)"
155 #define ALPHADIGIT        ALPHA DIGIT
156 #define ALPHADIGITS       "(["ALPHADIGIT"]+)"
157 #define ALPHADIGITDASH    ALPHA DIGIT "-"
158 #define ALPHADIGITDASHS   "(["ALPHADIGITDASH"]*)"
159
160 #define HOSTNUMBER        "("DIGITS"\\."DIGITS"\\."DIGITS"\\."DIGITS")"
161 #define TOPLABEL          ALPHAS"|(["ALPHA"]" ALPHADIGITDASHS "["ALPHADIGIT"])"
162 #define DOMAINLABEL       ALPHADIGITS"|(["ALPHADIGIT"]" ALPHADIGITDASHS \
163                                        "["ALPHADIGIT"])"
164 #define HOSTNAME          "((" DOMAINLABEL "\\.)+" TOPLABEL ")"
165 /* Based on http://www.ietf.org/rfc/rfc1738.txt (section 5) */
166 #define HOST              "("HOSTNAME "|" HOSTNUMBER")"
167 /* Based on http://www.ietf.org/rfc/rfc0822.txt (appendix D) */
168 #define EMAIL_LOCALPART   "([^\\(\\)<>@,;:\\\\\"\\[\\]\\s]+)"
169
170 /* UIN is digital according to the unofficial specification:
171  * http://iserverd.khstu.ru/docum_ext/icqv5.html#CTS
172  * 5 digits minimun according to http://en.wikipedia.org/wiki/ICQ#UIN */
173 #define ICQ_USER_NAME     "(["DIGIT"]{5,})"
174 /* Based on http://www.ietf.org/rfc/rfc2812.txt (section 2.3.1) */
175 #define IRC_SPECIAL       "_\\[\\]{}\\\\|`^"
176 #define IRC_USER_NAME     "(["ALPHA IRC_SPECIAL"]["ALPHADIGITDASH IRC_SPECIAL"]*)"
177 /* Based on http://www.ietf.org/rfc/rfc4622.txt (section 2.2)
178  * We just exclude invalid characters to avoid ucschars and other redundant
179  * complexity */
180 #define JABBER_USER_NAME  "([^@:'\"<>&\\s]+)"
181 /* ID is an email according to the unofficial specification:
182  * http://www.hypothetic.org/docs/msn/general/names.php */
183 #define MSN_USER_NAME     EMAIL_LOCALPART
184 /* Based on the official help:
185  * http://help.yahoo.com/l/us/yahoo/edit/registration/edit-01.html */
186 #define YAHOO_USER_NAME   "(["ALPHA"]["ALPHADIGIT"_\\.]{3,31})"
187
188 #define ACCOUNT_REGEX_ICQ      "^"ICQ_USER_NAME"$"
189 #define ACCOUNT_REGEX_IRC      "^"IRC_USER_NAME"$"
190 #define ACCOUNT_REGEX_JABBER   "^"JABBER_USER_NAME"@"HOST"$"
191 #define ACCOUNT_REGEX_MSN      "^"MSN_USER_NAME"@"HOST"$"
192 #define ACCOUNT_REGEX_YAHOO    "^"YAHOO_USER_NAME"$"
193
194 static void
195 account_widget_set_control_buttons_sensitivity (EmpathyAccountWidget *self,
196     gboolean sensitive)
197 {
198   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
199
200   if (!priv->simple)
201     {
202       /* we hit this case because of the 'other-accounts-exist' property handler
203        * being called during init (before constructed()) */
204       if (priv->apply_button == NULL || priv->cancel_button == NULL)
205         return;
206
207       gtk_widget_set_sensitive (priv->apply_button, sensitive);
208       gtk_widget_set_sensitive (priv->cancel_button,
209           (sensitive || priv->creating_account) && priv->other_accounts_exist);
210
211       if (sensitive)
212         {
213           /* We can't grab default if the widget hasn't be packed in a
214            * window */
215           GtkWidget *window;
216
217           window = gtk_widget_get_toplevel (priv->apply_button);
218           if (window != NULL &&
219               gtk_widget_is_toplevel (window))
220             {
221               gtk_widget_set_can_default (priv->apply_button, TRUE);
222               gtk_widget_grab_default (priv->apply_button);
223             }
224         }
225     }
226 }
227
228 static void
229 account_widget_set_entry_highlighting (GtkEntry *entry, gboolean highlight)
230 {
231   GdkColor color;
232   GtkStyle *style;
233
234   g_return_if_fail (GTK_IS_ENTRY (entry));
235
236   style = gtk_widget_get_style (GTK_WIDGET (entry));
237
238   if (highlight)
239     {
240       color = style->bg[GTK_STATE_SELECTED];
241
242       /* Here we take the current theme colour and add it to
243        * the colour for white and average the two. This
244        * gives a colour which is inline with the theme but
245        * slightly whiter.
246        */
247       color.red = (color.red + (style->white).red) / 2;
248       color.green = (color.green + (style->white).green) / 2;
249       color.blue = (color.blue + (style->white).blue) / 2;
250
251       gtk_widget_modify_base (GTK_WIDGET (entry), GTK_STATE_NORMAL, &color);
252     }
253   else
254     gtk_widget_modify_base (GTK_WIDGET (entry), GTK_STATE_NORMAL, NULL);
255 }
256
257 static void
258 account_widget_handle_control_buttons_sensitivity (EmpathyAccountWidget *self)
259 {
260   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
261   gboolean is_valid;
262
263   is_valid = empathy_account_settings_is_valid (priv->settings);
264
265   if (!priv->simple)
266       account_widget_set_control_buttons_sensitivity (self, is_valid);
267
268   g_signal_emit (self, signals[HANDLE_APPLY], 0, is_valid);
269 }
270
271 static void
272 account_widget_entry_changed_common (EmpathyAccountWidget *self,
273     GtkEntry *entry, gboolean focus)
274 {
275   const gchar *str;
276   const gchar *param_name;
277   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
278   gboolean prev_status;
279   gboolean curr_status;
280
281   str = gtk_entry_get_text (entry);
282   param_name = g_object_get_data (G_OBJECT (entry), "param_name");
283   prev_status = empathy_account_settings_parameter_is_valid (priv->settings,
284                                                              param_name);
285
286   if (EMP_STR_EMPTY (str))
287     {
288       const gchar *value = NULL;
289
290       empathy_account_settings_unset (priv->settings, param_name);
291
292       if (focus)
293         {
294           value = empathy_account_settings_get_string (priv->settings,
295               param_name);
296           DEBUG ("Unset %s and restore to %s", param_name, value);
297           gtk_entry_set_text (entry, value ? value : "");
298         }
299     }
300   else
301     {
302       DEBUG ("Setting %s to %s", param_name,
303           tp_strdiff (param_name, "password") ? str : "***");
304       empathy_account_settings_set_string (priv->settings, param_name, str);
305     }
306
307   curr_status = empathy_account_settings_parameter_is_valid (priv->settings,
308                                                              param_name);
309   if (curr_status != prev_status)
310     account_widget_set_entry_highlighting (entry, !curr_status);
311 }
312
313 static void
314 account_widget_entry_changed_cb (GtkEditable *entry,
315     EmpathyAccountWidget *self)
316 {
317   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
318
319   if (priv->automatic_change)
320     return;
321
322   account_widget_entry_changed_common (self, GTK_ENTRY (entry), FALSE);
323   empathy_account_widget_changed (self);
324 }
325
326 static void
327 account_widget_entry_map_cb (GtkEntry *entry,
328     EmpathyAccountWidget *self)
329 {
330   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
331   const gchar *param_name;
332   gboolean is_valid;
333
334   /* need to initialize input highlighting */
335   param_name = g_object_get_data (G_OBJECT (entry), "param_name");
336   is_valid = empathy_account_settings_parameter_is_valid (priv->settings,
337                                                           param_name);
338   account_widget_set_entry_highlighting (entry, !is_valid);
339 }
340
341 static void
342 account_widget_int_changed_cb (GtkWidget *widget,
343     EmpathyAccountWidget *self)
344 {
345   const gchar *param_name;
346   gint value;
347   const gchar *signature;
348   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
349
350   value = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
351   param_name = g_object_get_data (G_OBJECT (widget), "param_name");
352
353   signature = empathy_account_settings_get_dbus_signature (priv->settings,
354     param_name);
355   g_return_if_fail (signature != NULL);
356
357   DEBUG ("Setting %s to %d", param_name, value);
358
359   switch ((int)*signature)
360     {
361     case DBUS_TYPE_INT16:
362     case DBUS_TYPE_INT32:
363       empathy_account_settings_set_int32 (priv->settings, param_name, value);
364       break;
365     case DBUS_TYPE_INT64:
366       empathy_account_settings_set_int64 (priv->settings, param_name, value);
367       break;
368     case DBUS_TYPE_UINT16:
369     case DBUS_TYPE_UINT32:
370       empathy_account_settings_set_uint32 (priv->settings, param_name, value);
371       break;
372     case DBUS_TYPE_UINT64:
373       empathy_account_settings_set_uint64 (priv->settings, param_name, value);
374       break;
375     default:
376       g_return_if_reached ();
377     }
378
379   empathy_account_widget_changed (self);
380 }
381
382 static void
383 account_widget_checkbutton_toggled_cb (GtkWidget *widget,
384     EmpathyAccountWidget *self)
385 {
386   gboolean     value;
387   gboolean     default_value;
388   const gchar *param_name;
389   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
390
391   value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
392   param_name = g_object_get_data (G_OBJECT (widget), "param_name");
393
394   /* FIXME: This is ugly! checkbox don't have a "not-set" value so we
395    * always unset the param and set the value if different from the
396    * default value. */
397   empathy_account_settings_unset (priv->settings, param_name);
398   default_value = empathy_account_settings_get_boolean (priv->settings,
399       param_name);
400
401   if (default_value == value)
402     {
403       DEBUG ("Unset %s and restore to %d", param_name, default_value);
404     }
405   else
406     {
407       DEBUG ("Setting %s to %d", param_name, value);
408       empathy_account_settings_set_boolean (priv->settings, param_name, value);
409     }
410
411   empathy_account_widget_changed (self);
412 }
413
414 static void
415 account_widget_jabber_ssl_toggled_cb (GtkWidget *checkbutton_ssl,
416     EmpathyAccountWidget *self)
417 {
418   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
419   gboolean   value;
420   gint32       port = 0;
421
422   value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbutton_ssl));
423   port = empathy_account_settings_get_uint32 (priv->settings, "port");
424
425   if (value)
426     {
427       if (port == 5222 || port == 0)
428         port = 5223;
429     }
430   else
431     {
432       if (port == 5223 || port == 0)
433         port = 5222;
434     }
435
436   gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->spinbutton_port), port);
437
438   priv->contains_pending_changes = TRUE;
439 }
440
441 static void
442 account_widget_combobox_changed_cb (GtkWidget *widget,
443     EmpathyAccountWidget *self)
444 {
445   GtkTreeIter iter;
446   GtkTreeModel *model;
447   const gchar *value;
448   const GValue *v;
449   const gchar *default_value = NULL;
450   const gchar *param_name;
451   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
452
453   if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter))
454     return;
455
456   model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
457   /* the param value is stored in the first column */
458   gtk_tree_model_get (model, &iter, 0, &value, -1);
459
460   param_name = g_object_get_data (G_OBJECT (widget), "param_name");
461
462   v = empathy_account_settings_get_default (priv->settings, param_name);
463   if (v != NULL)
464     default_value = g_value_get_string (v);
465
466   if (!tp_strdiff (value, default_value))
467     {
468       DEBUG ("Unset %s and restore to %s", param_name, default_value);
469       empathy_account_settings_unset (priv->settings, param_name);
470     }
471   else
472     {
473       DEBUG ("Setting %s to %s", param_name, value);
474       empathy_account_settings_set_string (priv->settings, param_name, value);
475     }
476
477   empathy_account_widget_changed (self);
478 }
479
480 static void
481 clear_icon_released_cb (GtkEntry *entry,
482     GtkEntryIconPosition icon_pos,
483     GdkEvent *event,
484     EmpathyAccountWidget *self)
485 {
486   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
487   const gchar *param_name;
488
489   param_name = g_object_get_data (G_OBJECT (entry), "param_name");
490
491   DEBUG ("Unset %s", param_name);
492   empathy_account_settings_unset (priv->settings, param_name);
493   gtk_entry_set_text (entry, "");
494
495   empathy_account_widget_changed (self);
496 }
497
498 static void
499 password_entry_changed_cb (GtkEditable *entry,
500     EmpathyAccountWidget *self)
501 {
502   const gchar *str;
503
504   str = gtk_entry_get_text (GTK_ENTRY (entry));
505
506   gtk_entry_set_icon_sensitive (GTK_ENTRY (entry),
507       GTK_ENTRY_ICON_SECONDARY, !EMP_STR_EMPTY (str));
508 }
509
510 static void
511 password_entry_activated_cb (GtkEntry *entry,
512     EmpathyAccountWidget *self)
513 {
514     EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
515
516     if (gtk_widget_get_sensitive (priv->apply_button))
517         account_widget_apply_and_log_in (self);
518 }
519
520 static void
521 account_entry_activated_cb (GtkEntry *entry,
522     EmpathyAccountWidget *self)
523 {
524     EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
525
526     if (gtk_widget_get_sensitive (priv->apply_button))
527         account_widget_apply_and_log_in (self);
528 }
529
530 void
531 empathy_account_widget_setup_widget (EmpathyAccountWidget *self,
532     GtkWidget *widget,
533     const gchar *param_name)
534 {
535   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
536
537   g_object_set_data_full (G_OBJECT (widget), "param_name",
538       g_strdup (param_name), g_free);
539
540   if (GTK_IS_SPIN_BUTTON (widget))
541     {
542       gint value = 0;
543       const gchar *signature;
544
545       signature = empathy_account_settings_get_dbus_signature (priv->settings,
546           param_name);
547       g_return_if_fail (signature != NULL);
548
549       switch ((int)*signature)
550         {
551           case DBUS_TYPE_INT16:
552           case DBUS_TYPE_INT32:
553             value = empathy_account_settings_get_int32 (priv->settings,
554               param_name);
555             break;
556           case DBUS_TYPE_INT64:
557             value = empathy_account_settings_get_int64 (priv->settings,
558               param_name);
559             break;
560           case DBUS_TYPE_UINT16:
561           case DBUS_TYPE_UINT32:
562             value = empathy_account_settings_get_uint32 (priv->settings,
563               param_name);
564             break;
565           case DBUS_TYPE_UINT64:
566             value = empathy_account_settings_get_uint64 (priv->settings,
567                 param_name);
568             break;
569           default:
570             g_return_if_reached ();
571         }
572
573       gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value);
574
575       g_signal_connect (widget, "value-changed",
576           G_CALLBACK (account_widget_int_changed_cb),
577           self);
578     }
579   else if (GTK_IS_ENTRY (widget))
580     {
581       const gchar *str = NULL;
582
583       str = empathy_account_settings_get_string (priv->settings, param_name);
584       gtk_entry_set_text (GTK_ENTRY (widget), str ? str : "");
585
586       if (!tp_strdiff (param_name, "account"))
587         priv->param_account_widget = widget;
588       else if (!tp_strdiff (param_name, "password"))
589         priv->param_password_widget = widget;
590
591       if (strstr (param_name, "password"))
592         {
593           gtk_entry_set_visibility (GTK_ENTRY (widget), FALSE);
594
595           /* Add 'clear' icon */
596           gtk_entry_set_icon_from_stock (GTK_ENTRY (widget),
597               GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
598
599           gtk_entry_set_icon_sensitive (GTK_ENTRY (widget),
600               GTK_ENTRY_ICON_SECONDARY, !EMP_STR_EMPTY (str));
601
602           g_signal_connect (widget, "icon-release",
603               G_CALLBACK (clear_icon_released_cb), self);
604           g_signal_connect (widget, "changed",
605               G_CALLBACK (password_entry_changed_cb), self);
606           g_signal_connect (widget, "activate",
607               G_CALLBACK (password_entry_activated_cb), self);
608         }
609       else if (strstr (param_name, "account"))
610         g_signal_connect (widget, "activate",
611             G_CALLBACK (account_entry_activated_cb), self);
612
613
614       g_signal_connect (widget, "changed",
615           G_CALLBACK (account_widget_entry_changed_cb), self);
616       g_signal_connect (widget, "map",
617           G_CALLBACK (account_widget_entry_map_cb), self);
618     }
619   else if (GTK_IS_TOGGLE_BUTTON (widget))
620     {
621       gboolean value = FALSE;
622
623       value = empathy_account_settings_get_boolean (priv->settings,
624           param_name);
625       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value);
626
627       g_signal_connect (widget, "toggled",
628           G_CALLBACK (account_widget_checkbutton_toggled_cb),
629           self);
630     }
631   else if (GTK_IS_COMBO_BOX (widget))
632     {
633       /* The combo box's model has to contain the param value in its first
634        * column (as a string) */
635       const gchar *str;
636       GtkTreeModel *model;
637       GtkTreeIter iter;
638       gboolean valid;
639
640       str = empathy_account_settings_get_string (priv->settings, param_name);
641       model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
642
643       valid = gtk_tree_model_get_iter_first (model, &iter);
644       while (valid)
645         {
646           gchar *name;
647
648           gtk_tree_model_get (model, &iter, 0, &name, -1);
649           if (!tp_strdiff (name, str))
650             {
651               gtk_combo_box_set_active_iter (GTK_COMBO_BOX (widget), &iter);
652               valid = FALSE;
653             }
654           else
655             {
656               valid = gtk_tree_model_iter_next (model, &iter);
657             }
658
659           g_free (name);
660         }
661
662       g_signal_connect (widget, "changed",
663           G_CALLBACK (account_widget_combobox_changed_cb),
664           self);
665     }
666   else
667     {
668       DEBUG ("Unknown type of widget for param %s", param_name);
669     }
670 }
671
672 static GHashTable *
673 build_translated_params (void)
674 {
675   GHashTable *hash;
676
677   hash = g_hash_table_new (g_str_hash, g_str_equal);
678   g_hash_table_insert (hash, "account", _("Account"));
679   g_hash_table_insert (hash, "password", _("Password"));
680   g_hash_table_insert (hash, "server", _("Server"));
681   g_hash_table_insert (hash, "port", _("Port"));
682
683   return hash;
684 }
685
686 static gchar *
687 account_widget_generic_format_param_name (const gchar *param_name)
688 {
689   gchar *str;
690   gchar *p;
691   static GHashTable *translated_params = NULL;
692
693   if (G_UNLIKELY (translated_params == NULL))
694     translated_params = build_translated_params ();
695
696   /* Translate most common parameters */
697   str = g_hash_table_lookup (translated_params, param_name);
698   if (str != NULL)
699     return g_strdup (str);
700
701   str = g_strdup (param_name);
702
703   if (str && g_ascii_isalpha (str[0]))
704     str[0] = g_ascii_toupper (str[0]);
705
706   while ((p = strchr (str, '-')) != NULL)
707     {
708       if (p[1] != '\0' && g_ascii_isalpha (p[1]))
709         {
710           p[0] = ' ';
711           p[1] = g_ascii_toupper (p[1]);
712         }
713
714       p++;
715     }
716
717   return str;
718 }
719
720 static void
721 accounts_widget_generic_setup (EmpathyAccountWidget *self,
722     GtkWidget *table_common_settings,
723     GtkWidget *table_advanced_settings)
724 {
725   TpConnectionManagerParam *params, *param;
726   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
727
728   params = empathy_account_settings_get_tp_params (priv->settings);
729
730   for (param = params; param != NULL && param->name != NULL; param++)
731     {
732       GtkWidget       *table_settings;
733       guint            n_rows = 0;
734       GtkWidget       *widget = NULL;
735       gchar           *param_name_formatted;
736
737       if (param->flags & TP_CONN_MGR_PARAM_FLAG_REQUIRED)
738         table_settings = table_common_settings;
739       else if (priv->simple)
740         return;
741       else
742         table_settings = table_advanced_settings;
743
744       param_name_formatted = account_widget_generic_format_param_name
745         (param->name);
746       g_object_get (table_settings, "n-rows", &n_rows, NULL);
747       gtk_table_resize (GTK_TABLE (table_settings), ++n_rows, 2);
748
749       if (param->dbus_signature[0] == 's')
750         {
751           gchar *str;
752
753           str = g_strdup_printf (_("%s:"), param_name_formatted);
754           widget = gtk_label_new (str);
755           gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
756           g_free (str);
757
758           gtk_table_attach (GTK_TABLE (table_settings),
759               widget,
760               0, 1,
761               n_rows - 1, n_rows,
762               GTK_FILL, 0,
763               0, 0);
764           gtk_widget_show (widget);
765
766           widget = gtk_entry_new ();
767           if (strcmp (param->name, "account") == 0)
768             {
769               g_signal_connect (widget, "realize",
770                   G_CALLBACK (gtk_widget_grab_focus),
771                   NULL);
772             }
773           gtk_table_attach (GTK_TABLE (table_settings),
774               widget,
775               1, 2,
776               n_rows - 1, n_rows,
777               GTK_FILL | GTK_EXPAND, 0,
778               0, 0);
779           gtk_widget_show (widget);
780         }
781       /* int types: ynqiuxt. double type is 'd' */
782       else if (param->dbus_signature[0] == 'y' ||
783           param->dbus_signature[0] == 'n' ||
784           param->dbus_signature[0] == 'q' ||
785           param->dbus_signature[0] == 'i' ||
786           param->dbus_signature[0] == 'u' ||
787           param->dbus_signature[0] == 'x' ||
788           param->dbus_signature[0] == 't' ||
789           param->dbus_signature[0] == 'd')
790         {
791           gchar   *str = NULL;
792           gdouble  minint = 0;
793           gdouble  maxint = 0;
794           gdouble  step = 1;
795
796           switch (param->dbus_signature[0])
797             {
798             case 'y': minint = G_MININT8;  maxint = G_MAXINT8;   break;
799             case 'n': minint = G_MININT16; maxint = G_MAXINT16;  break;
800             case 'q': minint = 0;          maxint = G_MAXUINT16; break;
801             case 'i': minint = G_MININT32; maxint = G_MAXINT32;  break;
802             case 'u': minint = 0;          maxint = G_MAXUINT32; break;
803             case 'x': minint = G_MININT64; maxint = G_MAXINT64;  break;
804             case 't': minint = 0;          maxint = G_MAXUINT64; break;
805             case 'd': minint = G_MININT32; maxint = G_MAXINT32;
806               step = 0.1; break;
807             default: g_assert_not_reached ();
808             }
809
810           str = g_strdup_printf (_("%s:"), param_name_formatted);
811           widget = gtk_label_new (str);
812           gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
813           g_free (str);
814
815           gtk_table_attach (GTK_TABLE (table_settings),
816               widget,
817               0, 1,
818               n_rows - 1, n_rows,
819               GTK_FILL, 0,
820               0, 0);
821           gtk_widget_show (widget);
822
823           widget = gtk_spin_button_new_with_range (minint, maxint, step);
824           gtk_table_attach (GTK_TABLE (table_settings),
825               widget,
826               1, 2,
827               n_rows - 1, n_rows,
828               GTK_FILL | GTK_EXPAND, 0,
829               0, 0);
830           gtk_widget_show (widget);
831         }
832       else if (param->dbus_signature[0] == 'b')
833         {
834           widget = gtk_check_button_new_with_label (param_name_formatted);
835           gtk_table_attach (GTK_TABLE (table_settings),
836               widget,
837               0, 2,
838               n_rows - 1, n_rows,
839               GTK_FILL | GTK_EXPAND, 0,
840               0, 0);
841           gtk_widget_show (widget);
842         }
843       else
844         {
845           DEBUG ("Unknown signature for param %s: %s",
846               param_name_formatted, param->dbus_signature);
847         }
848
849       if (widget)
850         empathy_account_widget_setup_widget (self, widget, param->name);
851
852       g_free (param_name_formatted);
853     }
854 }
855
856 static void
857 account_widget_handle_params_valist (EmpathyAccountWidget *self,
858     const gchar *first_widget,
859     va_list args)
860 {
861   GObject *object;
862   const gchar *name;
863
864   for (name = first_widget; name; name = va_arg (args, const gchar *))
865     {
866       const gchar *param_name;
867
868       param_name = va_arg (args, const gchar *);
869       object = gtk_builder_get_object (self->ui_details->gui, name);
870
871       if (!object)
872         {
873           g_warning ("Builder is missing object '%s'.", name);
874           continue;
875         }
876
877       empathy_account_widget_setup_widget (self, GTK_WIDGET (object),
878           param_name);
879     }
880 }
881
882 static void
883 account_widget_cancel_clicked_cb (GtkWidget *button,
884     EmpathyAccountWidget *self)
885 {
886   g_signal_emit (self, signals[CANCELLED], 0);
887 }
888
889 static void
890 account_widget_account_enabled_cb (GObject *source_object,
891     GAsyncResult *res,
892     gpointer user_data)
893 {
894   GError *error = NULL;
895   TpAccount *account = TP_ACCOUNT (source_object);
896   EmpathyAccountWidget *widget = EMPATHY_ACCOUNT_WIDGET (user_data);
897   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
898
899   tp_account_set_enabled_finish (account, res, &error);
900
901   if (error != NULL)
902     {
903       DEBUG ("Could not enable the account: %s", error->message);
904       g_error_free (error);
905     }
906   else
907     {
908       empathy_connect_new_account (account, priv->account_manager);
909     }
910
911   /* unref widget - part of the workaround */
912   g_object_unref (widget);
913 }
914
915 static void
916 account_widget_applied_cb (GObject *source_object,
917     GAsyncResult *res,
918     gpointer user_data)
919 {
920   GError *error = NULL;
921   TpAccount *account;
922   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (source_object);
923   EmpathyAccountWidget *widget = EMPATHY_ACCOUNT_WIDGET (user_data);
924   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
925
926   empathy_account_settings_apply_finish (settings, res, &error);
927
928   if (error != NULL)
929     {
930       DEBUG ("Could not apply changes to account: %s", error->message);
931       g_error_free (error);
932       return;
933     }
934
935   account = empathy_account_settings_get_account (priv->settings);
936
937   if (account != NULL)
938     {
939       if (priv->creating_account)
940         {
941           /* By default, when an account is created, we enable it. */
942
943           /* workaround to keep widget alive during async call */
944           g_object_ref (widget);
945
946           tp_account_set_enabled_async (account, TRUE,
947               account_widget_account_enabled_cb, widget);
948           g_signal_emit (widget, signals[ACCOUNT_CREATED], 0, account);
949         }
950       else if (priv->enabled_checkbox != NULL)
951         {
952           gboolean enabled_checked;
953
954           enabled_checked =
955 #ifdef HAVE_MEEGO
956             mx_gtk_light_switch_get_active (
957                 MX_GTK_LIGHT_SWITCH (priv->enabled_checkbox));
958 #else
959             gtk_toggle_button_get_active (
960                 GTK_TOGGLE_BUTTON (priv->enabled_checkbox));
961 #endif /* HAVE_MEEGO */
962
963           if (tp_account_is_enabled (account) && enabled_checked)
964             {
965               /* After having applied changes to a user account, we
966                * automatically reconnect it. This is done so the new
967                * information entered by the user is validated on the server. */
968               tp_account_reconnect_async (account, NULL, NULL);
969             }
970         }
971     }
972
973   if (!priv->destroyed)
974     account_widget_set_control_buttons_sensitivity (widget, FALSE);
975
976   priv->contains_pending_changes = FALSE;
977
978   /* unref the widget - part of the workaround */
979   g_object_unref (widget);
980 }
981
982 static void
983 account_widget_apply_and_log_in (EmpathyAccountWidget *self)
984 {
985   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
986   gboolean display_name_overridden;
987
988   if (priv->radiobutton_reuse != NULL)
989     {
990       gboolean reuse = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
991             priv->radiobutton_reuse));
992
993       DEBUG ("Set register param: %d", !reuse);
994       empathy_account_settings_set_boolean (priv->settings, "register", !reuse);
995     }
996
997   g_object_get (priv->settings,
998       "display-name-overridden", &display_name_overridden, NULL);
999
1000   if (priv->creating_account || !display_name_overridden)
1001     {
1002       gchar *display_name;
1003
1004       /* set default display name for new accounts or update if user didn't
1005        * manually override it. */
1006       display_name = empathy_account_widget_get_default_display_name (self);
1007
1008       empathy_account_settings_set_display_name_async (priv->settings,
1009           display_name, NULL, NULL);
1010
1011       g_free (display_name);
1012     }
1013
1014   /* workaround to keep widget alive during async call */
1015   g_object_ref (self);
1016   empathy_account_settings_apply_async (priv->settings,
1017       account_widget_applied_cb, self);
1018 }
1019
1020 static void
1021 account_widget_apply_clicked_cb (GtkWidget *button,
1022     EmpathyAccountWidget *self)
1023 {
1024     account_widget_apply_and_log_in (self);
1025 }
1026
1027 static void
1028 account_widget_setup_generic (EmpathyAccountWidget *self)
1029 {
1030   GtkWidget *table_common_settings;
1031   GtkWidget *table_advanced_settings;
1032
1033   table_common_settings = GTK_WIDGET (gtk_builder_get_object
1034       (self->ui_details->gui, "table_common_settings"));
1035   table_advanced_settings = GTK_WIDGET (gtk_builder_get_object
1036       (self->ui_details->gui, "table_advanced_settings"));
1037
1038   accounts_widget_generic_setup (self, table_common_settings,
1039       table_advanced_settings);
1040
1041   g_object_unref (self->ui_details->gui);
1042 }
1043
1044 static void
1045 account_widget_settings_ready_cb (EmpathyAccountSettings *settings,
1046     GParamSpec *pspec,
1047     gpointer user_data)
1048 {
1049   EmpathyAccountWidget *self = user_data;
1050   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1051
1052   if (empathy_account_settings_is_ready (priv->settings))
1053     account_widget_setup_generic (self);
1054 }
1055
1056 static void
1057 account_widget_build_generic (EmpathyAccountWidget *self,
1058     const char *filename)
1059 {
1060   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1061   GtkWidget *expander_advanced;
1062
1063   self->ui_details->gui = empathy_builder_get_file (filename,
1064       "table_common_settings", &priv->table_common_settings,
1065       "vbox_generic_settings", &self->ui_details->widget,
1066       "expander_advanced_settings", &expander_advanced,
1067       NULL);
1068
1069   if (priv->simple)
1070     gtk_widget_hide (expander_advanced);
1071
1072   g_object_ref (self->ui_details->gui);
1073
1074   if (empathy_account_settings_is_ready (priv->settings))
1075     account_widget_setup_generic (self);
1076   else
1077     g_signal_connect (priv->settings, "notify::ready",
1078         G_CALLBACK (account_widget_settings_ready_cb), self);
1079 }
1080
1081 static void
1082 account_widget_launch_external_clicked (GtkWidget *button,
1083     TpAccount *account)
1084 {
1085   if (!tp_strdiff (tp_account_get_storage_provider (account),
1086         "com.meego.libsocialweb"))
1087     {
1088       /* we know how to handle this external provider */
1089       GDesktopAppInfo *app_info;
1090       const gchar *args[3] = { NULL, };
1091       GError *error = NULL;
1092
1093       app_info = g_desktop_app_info_new ("gnome-control-center.desktop");
1094
1095       if (app_info == NULL)
1096         {
1097           g_critical ("Could not locate 'gnome-control-center.desktop'");
1098           return;
1099         }
1100
1101       args[0] = g_app_info_get_commandline (G_APP_INFO (app_info));
1102       args[1] = "bisho.desktop";
1103       args[2] = NULL;
1104
1105       gdk_spawn_on_screen (gtk_widget_get_screen (button),
1106           NULL, (gchar **) args, NULL,
1107           G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error);
1108       if (error != NULL)
1109         {
1110           g_critical ("Failed to launch editor: %s", error->message);
1111           g_clear_error (&error);
1112         }
1113
1114       g_object_unref (app_info);
1115     }
1116 }
1117
1118 static void
1119 account_widget_build_external (EmpathyAccountWidget *self,
1120     EmpathyAccountSettings *settings)
1121 {
1122   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1123   TpAccount *account = empathy_account_settings_get_account (settings);
1124   GtkWidget *bar, *widget;
1125   gchar *str;
1126
1127   self->ui_details->widget = gtk_vbox_new (FALSE, 6);
1128   priv->table_common_settings = gtk_table_new (1, 2, FALSE);
1129
1130   if (!tp_strdiff (tp_account_get_storage_provider (account),
1131         "com.meego.libsocialweb"))
1132     {
1133       /* we know how to handle this external provider */
1134       str = g_strdup_printf (
1135           _("The account %s is edited via My Web Accounts."),
1136           empathy_account_settings_get_display_name (settings));
1137     }
1138   else
1139     {
1140       str = g_strdup_printf (
1141           _("The account %s cannot be edited in Empathy."),
1142           empathy_account_settings_get_display_name (settings));
1143     }
1144
1145   widget = gtk_label_new (str);
1146   gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
1147   g_free (str);
1148
1149   bar = gtk_info_bar_new ();
1150   gtk_info_bar_set_message_type (GTK_INFO_BAR (bar), GTK_MESSAGE_INFO);
1151   gtk_container_add (
1152       GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))),
1153       widget);
1154   gtk_container_set_border_width (GTK_CONTAINER (bar), 6);
1155
1156   if (!tp_strdiff (tp_account_get_storage_provider (account),
1157         "com.meego.libsocialweb"))
1158     {
1159       /* we know how to handle this external provider */
1160       widget = gtk_info_bar_add_button (GTK_INFO_BAR (bar),
1161           _("Launch My Web Accounts"), RESPONSE_LAUNCH);
1162
1163       g_signal_connect (widget, "clicked",
1164           G_CALLBACK (account_widget_launch_external_clicked), account);
1165     }
1166
1167   gtk_box_pack_start (GTK_BOX (self->ui_details->widget), bar,
1168       FALSE, TRUE, 0);
1169   gtk_box_pack_start (GTK_BOX (self->ui_details->widget),
1170       priv->table_common_settings, FALSE, TRUE, 0);
1171
1172   gtk_widget_show_all (self->ui_details->widget);
1173 }
1174
1175 static void
1176 account_widget_build_salut (EmpathyAccountWidget *self,
1177     const char *filename)
1178 {
1179   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1180   GtkWidget *expander_advanced;
1181
1182   self->ui_details->gui = empathy_builder_get_file (filename,
1183       "table_common_settings", &priv->table_common_settings,
1184       "vbox_salut_settings", &self->ui_details->widget,
1185       "expander_advanced_settings", &expander_advanced,
1186       NULL);
1187
1188   empathy_account_widget_handle_params (self,
1189       "entry_published", "published-name",
1190       "entry_nickname", "nickname",
1191       "entry_first_name", "first-name",
1192       "entry_last_name", "last-name",
1193       "entry_email", "email",
1194       "entry_jid", "jid",
1195       NULL);
1196
1197   if (priv->simple)
1198     gtk_widget_hide (expander_advanced);
1199
1200   self->ui_details->default_focus = g_strdup ("entry_first_name");
1201 }
1202
1203 static void
1204 account_widget_build_irc (EmpathyAccountWidget *self,
1205   const char *filename)
1206 {
1207   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1208
1209   empathy_account_settings_set_regex (priv->settings, "account",
1210       ACCOUNT_REGEX_IRC);
1211
1212   if (priv->simple)
1213     {
1214       priv->irc_network_chooser = empathy_account_widget_irc_build_simple (self,
1215           filename);
1216     }
1217   else
1218     {
1219       priv->irc_network_chooser = empathy_account_widget_irc_build (self,
1220           filename, &priv->table_common_settings);
1221     }
1222 }
1223
1224 static void
1225 account_widget_build_sip (EmpathyAccountWidget *self,
1226   const char *filename)
1227 {
1228   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1229   empathy_account_widget_sip_build (self, filename,
1230     &priv->table_common_settings);
1231
1232   if (priv->simple)
1233     {
1234       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1235               self->ui_details->gui, "remember_password_simple"));
1236     }
1237   else
1238     {
1239       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1240               self->ui_details->gui, "remember_password"));
1241     }
1242 }
1243
1244 static void
1245 account_widget_build_msn (EmpathyAccountWidget *self,
1246     const char *filename)
1247 {
1248   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1249
1250   empathy_account_settings_set_regex (priv->settings, "account",
1251       ACCOUNT_REGEX_MSN);
1252
1253   if (priv->simple)
1254     {
1255       self->ui_details->gui = empathy_builder_get_file (filename,
1256           "vbox_msn_simple", &self->ui_details->widget,
1257           NULL);
1258
1259       empathy_account_widget_handle_params (self,
1260           "entry_id_simple", "account",
1261           "entry_password_simple", "password",
1262           NULL);
1263
1264       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1265
1266       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1267               self->ui_details->gui, "remember_password_simple"));
1268     }
1269   else
1270     {
1271       self->ui_details->gui = empathy_builder_get_file (filename,
1272           "table_common_msn_settings", &priv->table_common_settings,
1273           "vbox_msn_settings", &self->ui_details->widget,
1274           NULL);
1275
1276       empathy_account_widget_handle_params (self,
1277           "entry_id", "account",
1278           "entry_password", "password",
1279           "entry_server", "server",
1280           "spinbutton_port", "port",
1281           NULL);
1282
1283       self->ui_details->default_focus = g_strdup ("entry_id");
1284
1285       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1286               self->ui_details->gui, "remember_password"));
1287     }
1288 }
1289
1290 static void
1291 suffix_id_widget_changed_cb (GtkWidget *entry,
1292     EmpathyAccountWidget *self)
1293 {
1294   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1295   const gchar *account;
1296
1297   g_assert (priv->jid_suffix != NULL);
1298
1299   account_widget_entry_changed_common (self, GTK_ENTRY (entry), FALSE);
1300
1301   account = empathy_account_settings_get_string (priv->settings, "account");
1302   if (!EMP_STR_EMPTY (account) &&
1303       !g_str_has_suffix (account, priv->jid_suffix))
1304     {
1305       gchar *tmp;
1306
1307       tmp = g_strdup_printf ("%s%s", account, priv->jid_suffix);
1308
1309       DEBUG ("Change account from '%s' to '%s'", account, tmp);
1310
1311       empathy_account_settings_set_string (priv->settings, "account", tmp);
1312       g_free (tmp);
1313     }
1314
1315   empathy_account_widget_changed (self);
1316 }
1317
1318 static gchar *
1319 remove_jid_suffix (EmpathyAccountWidget *self,
1320     const gchar *str)
1321 {
1322   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1323
1324   g_assert (priv->jid_suffix != NULL);
1325
1326   if (!g_str_has_suffix (str, priv->jid_suffix))
1327     return g_strdup (str);
1328
1329   return g_strndup (str, strlen (str) - strlen (priv->jid_suffix));
1330 }
1331
1332 static void
1333 setup_id_widget_with_suffix (EmpathyAccountWidget *self,
1334     GtkWidget *widget,
1335     const gchar *suffix)
1336 {
1337   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1338   const gchar *str = NULL;
1339
1340   g_object_set_data_full (G_OBJECT (widget), "param_name",
1341       g_strdup ("account"), g_free);
1342
1343   g_assert (priv->jid_suffix == NULL);
1344   priv->jid_suffix = g_strdup (suffix);
1345
1346   str = empathy_account_settings_get_string (priv->settings, "account");
1347   if (str != NULL)
1348     {
1349       gchar *tmp;
1350
1351       tmp = remove_jid_suffix (self, str);
1352       gtk_entry_set_text (GTK_ENTRY (widget), tmp);
1353       g_free (tmp);
1354     }
1355
1356   priv->param_account_widget = widget;
1357
1358   g_signal_connect (widget, "changed",
1359       G_CALLBACK (suffix_id_widget_changed_cb), self);
1360 }
1361
1362 static Service
1363 account_widget_get_service (EmpathyAccountWidget *self)
1364 {
1365   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1366   const gchar *icon_name, *service;
1367
1368   icon_name = empathy_account_settings_get_icon_name (priv->settings);
1369   service = empathy_account_settings_get_service (priv->settings);
1370
1371   /* Previous versions of Empathy didn't set the Service property on Facebook
1372    * and gtalk accounts, so we check using the icon name as well. */
1373   if (!tp_strdiff (icon_name, "im-google-talk") ||
1374       !tp_strdiff (service, "google-talk"))
1375     return GTALK_SERVICE;
1376
1377   if (!tp_strdiff (icon_name, "im-facebook") ||
1378       !tp_strdiff (service, "facebook"))
1379     return FACEBOOK_SERVICE;
1380
1381   return NO_SERVICE;
1382 }
1383
1384 static void
1385 account_widget_build_jabber (EmpathyAccountWidget *self,
1386     const char *filename)
1387 {
1388   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1389   GtkWidget *spinbutton_port;
1390   GtkWidget *checkbutton_ssl;
1391   GtkWidget *label_id, *label_password;
1392   GtkWidget *label_id_create, *label_password_create;
1393   GtkWidget *label_example_fb;
1394   GtkWidget *label_example;
1395   GtkWidget *expander_advanced;
1396   GtkWidget *entry_id;
1397   Service service;
1398
1399   service = account_widget_get_service (self);
1400
1401   empathy_account_settings_set_regex (priv->settings, "account",
1402       ACCOUNT_REGEX_JABBER);
1403
1404   if (priv->simple && service == NO_SERVICE)
1405     {
1406       /* Simple widget for XMPP */
1407       self->ui_details->gui = empathy_builder_get_file (filename,
1408           "vbox_jabber_simple", &self->ui_details->widget,
1409           "label_id_simple", &label_id,
1410           "label_id_create", &label_id_create,
1411           "label_password_simple", &label_password,
1412           "label_password_create", &label_password_create,
1413           NULL);
1414
1415       if (empathy_account_settings_get_boolean (priv->settings, "register"))
1416         {
1417           gtk_widget_hide (label_id);
1418           gtk_widget_hide (label_password);
1419           gtk_widget_show (label_id_create);
1420           gtk_widget_show (label_password_create);
1421         }
1422
1423       empathy_account_widget_handle_params (self,
1424           "entry_id_simple", "account",
1425           "entry_password_simple", "password",
1426           NULL);
1427
1428       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1429
1430       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1431               self->ui_details->gui, "remember_password_simple"));
1432     }
1433   else if (priv->simple && service == GTALK_SERVICE)
1434     {
1435       /* Simple widget for Google Talk */
1436       self->ui_details->gui = empathy_builder_get_file (filename,
1437           "vbox_gtalk_simple", &self->ui_details->widget,
1438           NULL);
1439
1440       empathy_account_widget_handle_params (self,
1441           "entry_id_g_simple", "account",
1442           "entry_password_g_simple", "password",
1443           NULL);
1444
1445       self->ui_details->default_focus = g_strdup ("entry_id_g_simple");
1446
1447       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1448               self->ui_details->gui, "remember_password_g_simple"));
1449     }
1450   else if (priv->simple && service == FACEBOOK_SERVICE)
1451     {
1452       /* Simple widget for Facebook */
1453       self->ui_details->gui = empathy_builder_get_file (filename,
1454           "vbox_fb_simple", &self->ui_details->widget,
1455           "entry_id_fb_simple", &entry_id,
1456           NULL);
1457
1458       empathy_account_widget_handle_params (self,
1459           "entry_password_fb_simple", "password",
1460           NULL);
1461
1462       setup_id_widget_with_suffix (self, entry_id, "@chat.facebook.com");
1463
1464       self->ui_details->default_focus = g_strdup ("entry_id_fb_simple");
1465
1466       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1467               self->ui_details->gui, "remember_password_fb_simple"));
1468     }
1469   else
1470     {
1471       ServiceInfo info = services_infos[service];
1472
1473       /* Full widget for XMPP, Google Talk and Facebook*/
1474       self->ui_details->gui = empathy_builder_get_file (filename,
1475           "table_common_settings", &priv->table_common_settings,
1476           "vbox_jabber_settings", &self->ui_details->widget,
1477           "spinbutton_port", &spinbutton_port,
1478           "checkbutton_ssl", &checkbutton_ssl,
1479           "label_username_f_example", &label_example_fb,
1480           info.label_username_example, &label_example,
1481           "expander_advanced", &expander_advanced,
1482           "entry_id", &entry_id,
1483           "label_id", &label_id,
1484           NULL);
1485
1486       empathy_account_widget_handle_params (self,
1487           "entry_password", "password",
1488           "entry_resource", "resource",
1489           "entry_server", "server",
1490           "spinbutton_port", "port",
1491           "spinbutton_priority", "priority",
1492           "checkbutton_ssl", "old-ssl",
1493           "checkbutton_ignore_ssl_errors", "ignore-ssl-errors",
1494           "checkbutton_encryption", "require-encryption",
1495           NULL);
1496
1497       if (service == FACEBOOK_SERVICE)
1498         {
1499           gtk_label_set_label (GTK_LABEL (label_id), _("Username:"));
1500
1501           /* Facebook special case the entry ID widget to hide the
1502            * "@chat.facebook.com" part */
1503           setup_id_widget_with_suffix (self, entry_id, "@chat.facebook.com");
1504         }
1505       else
1506         {
1507           empathy_account_widget_setup_widget (self, entry_id, "account");
1508         }
1509
1510       self->ui_details->default_focus = g_strdup ("entry_id");
1511       priv->spinbutton_port = spinbutton_port;
1512
1513       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1514               self->ui_details->gui, "remember_password"));
1515
1516       g_signal_connect (checkbutton_ssl, "toggled",
1517           G_CALLBACK (account_widget_jabber_ssl_toggled_cb),
1518           self);
1519
1520       if (service == FACEBOOK_SERVICE)
1521         {
1522           GtkContainer *parent;
1523           GList *children;
1524
1525           /* Removing the label from list of focusable widgets */
1526           parent = GTK_CONTAINER (gtk_widget_get_parent (label_example_fb));
1527           children = gtk_container_get_children (parent);
1528           children = g_list_remove (children, label_example_fb);
1529           gtk_container_set_focus_chain (parent, children);
1530           g_list_free (children);
1531         }
1532
1533       gtk_widget_show (label_example);
1534
1535       if (!info.show_advanced)
1536         gtk_widget_hide (expander_advanced);
1537     }
1538 }
1539
1540 static void
1541 account_widget_build_icq (EmpathyAccountWidget *self,
1542     const char *filename)
1543 {
1544   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1545   GtkWidget *spinbutton_port;
1546
1547   empathy_account_settings_set_regex (priv->settings, "account",
1548       ACCOUNT_REGEX_ICQ);
1549
1550   if (priv->simple)
1551     {
1552       self->ui_details->gui = empathy_builder_get_file (filename,
1553           "vbox_icq_simple", &self->ui_details->widget,
1554           NULL);
1555
1556       empathy_account_widget_handle_params (self,
1557           "entry_uin_simple", "account",
1558           "entry_password_simple", "password",
1559           NULL);
1560
1561       self->ui_details->default_focus = g_strdup ("entry_uin_simple");
1562
1563       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1564               self->ui_details->gui, "remember_password_simple"));
1565     }
1566   else
1567     {
1568       self->ui_details->gui = empathy_builder_get_file (filename,
1569           "table_common_settings", &priv->table_common_settings,
1570           "vbox_icq_settings", &self->ui_details->widget,
1571           "spinbutton_port", &spinbutton_port,
1572           NULL);
1573
1574       empathy_account_widget_handle_params (self,
1575           "entry_uin", "account",
1576           "entry_password", "password",
1577           "entry_server", "server",
1578           "spinbutton_port", "port",
1579           "entry_charset", "charset",
1580           NULL);
1581
1582       self->ui_details->default_focus = g_strdup ("entry_uin");
1583
1584       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1585               self->ui_details->gui, "remember_password"));
1586     }
1587 }
1588
1589 static void
1590 account_widget_build_aim (EmpathyAccountWidget *self,
1591     const char *filename)
1592 {
1593   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1594   GtkWidget *spinbutton_port;
1595
1596   if (priv->simple)
1597     {
1598       self->ui_details->gui = empathy_builder_get_file (filename,
1599           "vbox_aim_simple", &self->ui_details->widget,
1600           NULL);
1601
1602       empathy_account_widget_handle_params (self,
1603           "entry_screenname_simple", "account",
1604           "entry_password_simple", "password",
1605           NULL);
1606
1607       self->ui_details->default_focus = g_strdup ("entry_screenname_simple");
1608
1609       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1610               self->ui_details->gui, "remember_password_simple"));
1611     }
1612   else
1613     {
1614       self->ui_details->gui = empathy_builder_get_file (filename,
1615           "table_common_settings", &priv->table_common_settings,
1616           "vbox_aim_settings", &self->ui_details->widget,
1617           "spinbutton_port", &spinbutton_port,
1618           NULL);
1619
1620       empathy_account_widget_handle_params (self,
1621           "entry_screenname", "account",
1622           "entry_password", "password",
1623           "entry_server", "server",
1624           "spinbutton_port", "port",
1625           NULL);
1626
1627       self->ui_details->default_focus = g_strdup ("entry_screenname");
1628
1629       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1630               self->ui_details->gui, "remember_password"));
1631     }
1632 }
1633
1634 static void
1635 account_widget_build_yahoo (EmpathyAccountWidget *self,
1636     const char *filename)
1637 {
1638   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1639
1640   empathy_account_settings_set_regex (priv->settings, "account",
1641       ACCOUNT_REGEX_YAHOO);
1642
1643   if (priv->simple)
1644     {
1645       self->ui_details->gui = empathy_builder_get_file (filename,
1646           "vbox_yahoo_simple", &self->ui_details->widget,
1647           NULL);
1648
1649       empathy_account_widget_handle_params (self,
1650           "entry_id_simple", "account",
1651           "entry_password_simple", "password",
1652           NULL);
1653
1654       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1655
1656       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1657               self->ui_details->gui, "remember_password_simple"));
1658     }
1659   else
1660     {
1661       self->ui_details->gui = empathy_builder_get_file (filename,
1662           "table_common_settings", &priv->table_common_settings,
1663           "vbox_yahoo_settings", &self->ui_details->widget,
1664           NULL);
1665
1666       empathy_account_widget_handle_params (self,
1667           "entry_id", "account",
1668           "entry_password", "password",
1669           "entry_server", "server",
1670           "entry_locale", "room-list-locale",
1671           "entry_charset", "charset",
1672           "spinbutton_port", "port",
1673           "checkbutton_ignore_invites", "ignore-invites",
1674           NULL);
1675
1676       self->ui_details->default_focus = g_strdup ("entry_id");
1677
1678       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1679               self->ui_details->gui, "remember_password"));
1680     }
1681 }
1682
1683 static void
1684 account_widget_build_groupwise (EmpathyAccountWidget *self,
1685     const char *filename)
1686 {
1687   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1688
1689   if (priv->simple)
1690     {
1691       self->ui_details->gui = empathy_builder_get_file (filename,
1692           "vbox_groupwise_simple", &self->ui_details->widget,
1693           NULL);
1694
1695       empathy_account_widget_handle_params (self,
1696           "entry_id_simple", "account",
1697           "entry_password_simple", "password",
1698           NULL);
1699
1700       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1701
1702       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1703               self->ui_details->gui, "remember_password_simple"));
1704     }
1705   else
1706     {
1707       self->ui_details->gui = empathy_builder_get_file (filename,
1708           "table_common_groupwise_settings", &priv->table_common_settings,
1709           "vbox_groupwise_settings", &self->ui_details->widget,
1710           NULL);
1711
1712       empathy_account_widget_handle_params (self,
1713           "entry_id", "account",
1714           "entry_password", "password",
1715           "entry_server", "server",
1716           "spinbutton_port", "port",
1717           NULL);
1718
1719       self->ui_details->default_focus = g_strdup ("entry_id");
1720
1721       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1722               self->ui_details->gui, "remember_password"));
1723     }
1724 }
1725
1726 static void
1727 account_widget_destroy_cb (GtkWidget *widget,
1728     EmpathyAccountWidget *self)
1729 {
1730   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1731   /* set the destroyed flag - workaround */
1732   priv->destroyed = TRUE;
1733
1734   g_object_unref (self);
1735 }
1736
1737 static void
1738 empathy_account_widget_enabled_cb (TpAccount *account,
1739       GParamSpec *spec,
1740       gpointer user_data)
1741 {
1742   EmpathyAccountWidget *widget = EMPATHY_ACCOUNT_WIDGET (user_data);
1743   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
1744   gboolean enabled = tp_account_is_enabled (account);
1745
1746   if (priv->enabled_checkbox != NULL)
1747     {
1748 #ifdef HAVE_MEEGO
1749       mx_gtk_light_switch_set_active (
1750           MX_GTK_LIGHT_SWITCH (priv->enabled_checkbox),
1751           enabled);
1752 #else
1753       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->enabled_checkbox),
1754           enabled);
1755 #endif /* HAVE_MEEGO */
1756     }
1757 }
1758
1759 static void
1760 #ifdef HAVE_MEEGO
1761 account_widget_switch_flipped_cb (MxGtkLightSwitch *sw,
1762     gboolean state,
1763     gpointer user_data)
1764 #else
1765 account_widget_enabled_toggled_cb (GtkToggleButton *toggle_button,
1766     gpointer user_data)
1767 #endif /* HAVE_MEEGO */
1768 {
1769   EmpathyAccountWidgetPriv *priv = GET_PRIV (user_data);
1770   TpAccount *account;
1771 #ifndef HAVE_MEEGO
1772   gboolean state;
1773
1774   state = gtk_toggle_button_get_active (toggle_button);
1775 #endif /* HAVE_MEEGO */
1776
1777   account = empathy_account_settings_get_account (priv->settings);
1778
1779   /* Enable the account according to the value of the "Enabled" checkbox */
1780   /* workaround to keep widget alive during async call */
1781   g_object_ref (user_data);
1782   tp_account_set_enabled_async (account, state,
1783       account_widget_account_enabled_cb, user_data);
1784 }
1785
1786 void
1787 empathy_account_widget_set_other_accounts_exist (EmpathyAccountWidget *self,
1788     gboolean others_exist)
1789 {
1790   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1791
1792   priv->other_accounts_exist = others_exist;
1793
1794   if (priv->creating_account)
1795     account_widget_handle_control_buttons_sensitivity (self);
1796 }
1797
1798 static void
1799 do_set_property (GObject *object,
1800     guint prop_id,
1801     const GValue *value,
1802     GParamSpec *pspec)
1803 {
1804   EmpathyAccountWidgetPriv *priv = GET_PRIV (object);
1805
1806   switch (prop_id)
1807     {
1808     case PROP_SETTINGS:
1809       priv->settings = g_value_dup_object (value);
1810       break;
1811     case PROP_SIMPLE:
1812       priv->simple = g_value_get_boolean (value);
1813       break;
1814     case PROP_CREATING_ACCOUNT:
1815       priv->creating_account = g_value_get_boolean (value);
1816       break;
1817     case PROP_OTHER_ACCOUNTS_EXIST:
1818       empathy_account_widget_set_other_accounts_exist (
1819           EMPATHY_ACCOUNT_WIDGET (object), g_value_get_boolean (value));
1820       break;
1821     default:
1822       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1823     }
1824 }
1825
1826 static void
1827 do_get_property (GObject *object,
1828     guint prop_id,
1829     GValue *value,
1830     GParamSpec *pspec)
1831 {
1832   EmpathyAccountWidgetPriv *priv = GET_PRIV (object);
1833
1834   switch (prop_id)
1835     {
1836     case PROP_PROTOCOL:
1837       g_value_set_string (value,
1838         empathy_account_settings_get_protocol (priv->settings));
1839       break;
1840     case PROP_SETTINGS:
1841       g_value_set_object (value, priv->settings);
1842       break;
1843     case PROP_SIMPLE:
1844       g_value_set_boolean (value, priv->simple);
1845       break;
1846     case PROP_CREATING_ACCOUNT:
1847       g_value_set_boolean (value, priv->creating_account);
1848       break;
1849     case PROP_OTHER_ACCOUNTS_EXIST:
1850       g_value_set_boolean (value, priv->other_accounts_exist);
1851       break;
1852     default:
1853       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1854     }
1855 }
1856
1857 static void
1858 set_apply_button (EmpathyAccountWidget *self)
1859 {
1860   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1861   GtkWidget *image;
1862
1863   /* We can't use the stock button as its accelerator ('A') clashes with the
1864    * Add button. */
1865   gtk_button_set_use_stock (GTK_BUTTON (priv->apply_button), FALSE);
1866
1867   gtk_button_set_label (GTK_BUTTON (priv->apply_button), _("A_pply"));
1868   gtk_button_set_use_underline (GTK_BUTTON (priv->apply_button), TRUE);
1869
1870   image = gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
1871   gtk_button_set_image (GTK_BUTTON (priv->apply_button), image);
1872 }
1873
1874 static void
1875 presence_changed_cb (TpAccountManager *manager,
1876     TpConnectionPresenceType state,
1877     const gchar *status,
1878     const gchar *message,
1879     EmpathyAccountWidget *self)
1880 {
1881   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1882
1883   if (priv->destroyed)
1884     return;
1885
1886   if (priv->apply_button == NULL)
1887     /* This button doesn't exist in 'simple' mode */
1888     return;
1889
1890   if (state > TP_CONNECTION_PRESENCE_TYPE_OFFLINE &&
1891       priv->creating_account)
1892     {
1893       /* We are online and creating a new account, display a Login button */
1894       GtkWidget *image;
1895
1896       gtk_button_set_use_stock (GTK_BUTTON (priv->apply_button), FALSE);
1897       gtk_button_set_label (GTK_BUTTON (priv->apply_button), _("L_og in"));
1898
1899       image = gtk_image_new_from_stock (GTK_STOCK_CONNECT,
1900           GTK_ICON_SIZE_BUTTON);
1901       gtk_button_set_image (GTK_BUTTON (priv->apply_button), image);
1902     }
1903   else
1904     {
1905       /* We are offline or modifying an existing account, display
1906        * a Save button */
1907       set_apply_button (self);
1908     }
1909 }
1910
1911 static void
1912 account_manager_ready_cb (GObject *source_object,
1913     GAsyncResult *result,
1914     gpointer user_data)
1915 {
1916   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (user_data);
1917   TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
1918   GError *error = NULL;
1919   TpConnectionPresenceType state;
1920
1921   if (!tp_account_manager_prepare_finish (account_manager, result, &error))
1922     {
1923       DEBUG ("Failed to prepare account manager: %s", error->message);
1924       g_error_free (error);
1925       goto out;
1926     }
1927
1928   state = tp_account_manager_get_most_available_presence (account_manager, NULL,
1929       NULL);
1930
1931   /* simulate a presence change so the apply button will be changed
1932    * if needed */
1933   presence_changed_cb (account_manager, state, NULL, NULL, self);
1934
1935 out:
1936   g_object_unref (self);
1937 }
1938
1939 #define WIDGET(cm, proto) \
1940   { #cm, #proto, "empathy-account-widget-"#proto".ui", \
1941     account_widget_build_##proto }
1942
1943 static void
1944 add_enable_checkbox (EmpathyAccountWidget *self,
1945     TpAccount *account)
1946 {
1947   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1948 #ifdef HAVE_MEEGO
1949   GtkWidget *w;
1950 #else
1951   GtkWidget *vbox = self->ui_details->widget;
1952 #endif /* HAVE_MEEGO */
1953   guint nb_rows, nb_columns;
1954   gboolean is_enabled;
1955
1956   /* handle the "Enabled" checkbox. We only add it when modifying an account */
1957   if (priv->creating_account || priv->table_common_settings == NULL)
1958     return;
1959
1960   is_enabled = tp_account_is_enabled (account);
1961
1962 #ifdef HAVE_MEEGO
1963   w = gtk_label_new (_("Account:"));
1964   gtk_misc_set_alignment (GTK_MISC (w), 0, 0.5);
1965
1966   priv->enabled_checkbox = mx_gtk_light_switch_new ();
1967
1968   mx_gtk_light_switch_set_active (
1969       MX_GTK_LIGHT_SWITCH (priv->enabled_checkbox), is_enabled);
1970
1971   gtk_widget_show (w);
1972 #else
1973   priv->enabled_checkbox =
1974       gtk_check_button_new_with_mnemonic (_("_Enabled"));
1975
1976   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->enabled_checkbox),
1977       is_enabled);
1978 #endif /* HAVE_MEEGO */
1979
1980   g_object_get (priv->table_common_settings, "n-rows", &nb_rows,
1981       "n-columns", &nb_columns, NULL);
1982
1983   gtk_table_resize (GTK_TABLE (priv->table_common_settings), ++nb_rows,
1984       nb_columns);
1985
1986 #ifdef HAVE_MEEGO
1987   gtk_table_attach (GTK_TABLE (priv->table_common_settings),
1988       w,
1989       0, 1, nb_rows - 1, nb_rows,
1990       GTK_FILL, 0, 0, 0);
1991   gtk_table_attach (GTK_TABLE (priv->table_common_settings),
1992       priv->enabled_checkbox,
1993       1, nb_columns, nb_rows - 1, nb_rows,
1994       GTK_EXPAND | GTK_FILL, 0, 0, 0);
1995 #else
1996   gtk_box_pack_start (GTK_BOX (vbox), priv->enabled_checkbox, FALSE, FALSE, 0);
1997   gtk_box_reorder_child (GTK_BOX (vbox), priv->enabled_checkbox, 0);
1998 #endif /* HAVE_MEEGO */
1999
2000   gtk_widget_show (priv->enabled_checkbox);
2001
2002 #ifdef HAVE_MEEGO
2003   g_signal_connect (G_OBJECT (priv->enabled_checkbox), "switch-flipped",
2004       G_CALLBACK (account_widget_switch_flipped_cb), self);
2005 #else
2006   g_signal_connect (G_OBJECT (priv->enabled_checkbox), "toggled",
2007       G_CALLBACK (account_widget_enabled_toggled_cb), self);
2008 #endif /* HAVE_MEEGO */
2009 }
2010
2011 #ifndef HAVE_MEEGO
2012 /* Meego doesn't support registration */
2013 static void
2014 add_register_buttons (EmpathyAccountWidget *self,
2015     TpAccount *account)
2016 {
2017   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2018   const TpConnectionManagerProtocol *protocol;
2019   GtkWidget *radiobutton_register;
2020   GtkWidget *vbox = self->ui_details->widget;
2021
2022   if (!priv->creating_account)
2023     return;
2024
2025   protocol = empathy_account_settings_get_tp_protocol (priv->settings);
2026   if (protocol == NULL)
2027     return;
2028
2029   if (!tp_connection_manager_protocol_can_register (protocol))
2030     return;
2031
2032   if (account_widget_get_service (self) != NO_SERVICE)
2033     return;
2034
2035   if (priv->simple)
2036     return;
2037
2038   priv->radiobutton_reuse = gtk_radio_button_new_with_label (NULL,
2039       _("This account already exists on the server"));
2040   radiobutton_register = gtk_radio_button_new_with_label (
2041       gtk_radio_button_get_group (GTK_RADIO_BUTTON (priv->radiobutton_reuse)),
2042       _("Create a new account on the server"));
2043
2044   gtk_box_pack_start (GTK_BOX (vbox), priv->radiobutton_reuse, FALSE, FALSE, 0);
2045   gtk_box_pack_start (GTK_BOX (vbox), radiobutton_register, FALSE, FALSE, 0);
2046   gtk_box_reorder_child (GTK_BOX (vbox), priv->radiobutton_reuse, 0);
2047   gtk_box_reorder_child (GTK_BOX (vbox), radiobutton_register, 1);
2048   gtk_widget_show (priv->radiobutton_reuse);
2049   gtk_widget_show (radiobutton_register);
2050 }
2051 #endif /* HAVE_MEEGO */
2052
2053 static void
2054 remember_password_toggled_cb (GtkToggleButton *button,
2055     EmpathyAccountWidget *self)
2056 {
2057   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2058
2059   if (gtk_toggle_button_get_active (button))
2060     {
2061       gtk_widget_set_sensitive (priv->param_password_widget, TRUE);
2062     }
2063   else
2064     {
2065       gtk_widget_set_sensitive (priv->param_password_widget, FALSE);
2066       gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), "");
2067       empathy_account_settings_unset (priv->settings, "password");
2068     }
2069 }
2070
2071 static void
2072 account_settings_password_retrieved_cb (GObject *object,
2073     gpointer user_data)
2074 {
2075   EmpathyAccountWidget *self = user_data;
2076   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2077   const gchar *password = empathy_account_settings_get_string (
2078       priv->settings, "password");
2079
2080   if (password != NULL)
2081     {
2082       /* We have to do this so that when we call gtk_entry_set_text,
2083        * the ::changed callback doesn't think the user made the
2084        * change. */
2085       priv->automatic_change = TRUE;
2086       gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), password);
2087       priv->automatic_change = FALSE;
2088     }
2089
2090   gtk_toggle_button_set_active (
2091       GTK_TOGGLE_BUTTON (priv->remember_password_widget),
2092       !EMP_STR_EMPTY (password));
2093 }
2094
2095 static void
2096 do_constructed (GObject *obj)
2097 {
2098   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2099   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2100   TpAccount *account;
2101   TpStorageRestrictionFlags storage_restrictions;
2102   const gchar *display_name, *default_display_name;
2103   guint i = 0;
2104   struct {
2105     const gchar *cm_name;
2106     const gchar *protocol;
2107     const char *file;
2108     void (*func)(EmpathyAccountWidget *self, const gchar *filename);
2109   } widgets [] = {
2110     { "salut", "local-xmpp", "empathy-account-widget-local-xmpp.ui",
2111         account_widget_build_salut },
2112     WIDGET (gabble, jabber),
2113     WIDGET (butterfly, msn),
2114     WIDGET (haze, icq),
2115     WIDGET (haze, aim),
2116     WIDGET (haze, yahoo),
2117     WIDGET (haze, groupwise),
2118     WIDGET (idle, irc),
2119     WIDGET (sofiasip, sip),
2120   };
2121
2122   account = empathy_account_settings_get_account (priv->settings);
2123
2124   if (account != NULL)
2125     storage_restrictions = tp_account_get_storage_restrictions (account);
2126   else
2127     storage_restrictions = 0;
2128
2129   /* Empathy can only edit accounts without the Cannot_Set_Parameters flag */
2130   if (storage_restrictions & TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS)
2131     {
2132       DEBUG ("Account is provided by an external storage provider");
2133
2134       account_widget_build_external (self, priv->settings);
2135     }
2136   else
2137     {
2138       const gchar *protocol, *cm_name;
2139
2140       cm_name = empathy_account_settings_get_cm (priv->settings);
2141       protocol = empathy_account_settings_get_protocol (priv->settings);
2142
2143       for (i = 0 ; i < G_N_ELEMENTS (widgets); i++)
2144         {
2145           if (!tp_strdiff (widgets[i].cm_name, cm_name) &&
2146               !tp_strdiff (widgets[i].protocol, protocol))
2147             {
2148               gchar *filename;
2149
2150               filename = empathy_file_lookup (widgets[i].file,
2151                   "libempathy-gtk");
2152               widgets[i].func (self, filename);
2153               g_free (filename);
2154
2155               break;
2156             }
2157         }
2158
2159       if (i == G_N_ELEMENTS (widgets))
2160         {
2161           gchar *filename = empathy_file_lookup (
2162               "empathy-account-widget-generic.ui", "libempathy-gtk");
2163           account_widget_build_generic (self, filename);
2164           g_free (filename);
2165         }
2166     }
2167
2168   /* handle default focus */
2169   if (self->ui_details->default_focus != NULL)
2170     {
2171       GObject *default_focus_entry;
2172
2173       default_focus_entry = gtk_builder_get_object
2174         (self->ui_details->gui, self->ui_details->default_focus);
2175       g_signal_connect (default_focus_entry, "realize",
2176           G_CALLBACK (gtk_widget_grab_focus),
2177           NULL);
2178     }
2179
2180   /* remember password */
2181   if (priv->param_password_widget != NULL
2182       && priv->remember_password_widget != NULL
2183       && empathy_account_settings_supports_sasl (priv->settings))
2184     {
2185       if (priv->simple)
2186         {
2187           gtk_toggle_button_set_active (
2188               GTK_TOGGLE_BUTTON (priv->remember_password_widget), TRUE);
2189         }
2190       else
2191         {
2192           gtk_toggle_button_set_active (
2193               GTK_TOGGLE_BUTTON (priv->remember_password_widget),
2194               !EMP_STR_EMPTY (empathy_account_settings_get_string (
2195                       priv->settings, "password")));
2196
2197           /* The password might not have been retrieved from the
2198            * keyring yet. We should update the remember password
2199            * toggle button and the password entry when/if it is. */
2200           g_signal_connect (priv->settings, "password-retrieved",
2201               G_CALLBACK (account_settings_password_retrieved_cb), self);
2202         }
2203
2204       g_signal_connect (priv->remember_password_widget, "toggled",
2205           G_CALLBACK (remember_password_toggled_cb), self);
2206
2207       remember_password_toggled_cb (
2208           GTK_TOGGLE_BUTTON (priv->remember_password_widget), self);
2209     }
2210   else if (priv->remember_password_widget != NULL
2211       && !empathy_account_settings_supports_sasl (priv->settings))
2212     {
2213       gtk_widget_set_visible (priv->remember_password_widget, FALSE);
2214     }
2215
2216   /* dup and init the account-manager */
2217   priv->account_manager = tp_account_manager_dup ();
2218
2219   g_object_ref (self);
2220   tp_account_manager_prepare_async (priv->account_manager, NULL,
2221       account_manager_ready_cb, self);
2222
2223   /* handle apply and cancel button */
2224   if (!priv->simple &&
2225       !(storage_restrictions &
2226         TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS))
2227     {
2228       GtkWidget *hbox = gtk_hbox_new (TRUE, 3);
2229       GtkWidget *image;
2230
2231       /*  We can't use the stock button as its accelerator ('C') clashes with
2232        *  the Close button. */
2233       priv->cancel_button = gtk_button_new ();
2234       gtk_button_set_label (GTK_BUTTON (priv->cancel_button), _("Ca_ncel"));
2235       gtk_button_set_use_underline (GTK_BUTTON (priv->cancel_button), TRUE);
2236
2237       image = gtk_image_new_from_stock (GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
2238       gtk_button_set_image (GTK_BUTTON (priv->cancel_button), image);
2239
2240       priv->apply_button = gtk_button_new ();
2241       set_apply_button (self);
2242
2243       /* We'll change this button to a "Log in" one if we are creating a new
2244        * account and are connected. */
2245       tp_g_signal_connect_object (priv->account_manager,
2246           "most-available-presence-changed",
2247           G_CALLBACK (presence_changed_cb), obj, 0);
2248
2249       gtk_box_pack_end (GTK_BOX (hbox), priv->apply_button, TRUE,
2250           TRUE, 3);
2251       gtk_box_pack_end (GTK_BOX (hbox), priv->cancel_button, TRUE,
2252           TRUE, 3);
2253
2254       gtk_box_pack_end (GTK_BOX (self->ui_details->widget), hbox, FALSE,
2255           FALSE, 3);
2256
2257       g_signal_connect (priv->cancel_button, "clicked",
2258           G_CALLBACK (account_widget_cancel_clicked_cb),
2259           self);
2260       g_signal_connect (priv->apply_button, "clicked",
2261           G_CALLBACK (account_widget_apply_clicked_cb),
2262           self);
2263       gtk_widget_show_all (hbox);
2264
2265       if (priv->creating_account)
2266         /* When creating an account, the user might have nothing to enter.
2267          * That means that no control interaction might occur,
2268          * so we update the control button sensitivity manually.
2269          */
2270         account_widget_handle_control_buttons_sensitivity (self);
2271       else
2272         account_widget_set_control_buttons_sensitivity (self, FALSE);
2273     }
2274
2275   if (account != NULL)
2276     {
2277       g_signal_connect (account, "notify::enabled",
2278           G_CALLBACK (empathy_account_widget_enabled_cb), self);
2279     }
2280
2281 #ifndef HAVE_MEEGO
2282   add_register_buttons (self, account);
2283 #endif /* HAVE_MEEGO */
2284
2285   /* add the Enable checkbox to accounts that support it */
2286   if (!(storage_restrictions & TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_ENABLED))
2287     add_enable_checkbox (self, account);
2288
2289   /* hook up to widget destruction to unref ourselves */
2290   g_signal_connect (self->ui_details->widget, "destroy",
2291       G_CALLBACK (account_widget_destroy_cb), self);
2292
2293   if (self->ui_details->gui != NULL)
2294     {
2295       empathy_builder_unref_and_keep_widget (self->ui_details->gui,
2296           self->ui_details->widget);
2297       self->ui_details->gui = NULL;
2298     }
2299
2300   display_name = empathy_account_settings_get_display_name (priv->settings);
2301   default_display_name = empathy_account_widget_get_default_display_name (self);
2302
2303   if (tp_strdiff (display_name, default_display_name) &&
2304       !priv->creating_account)
2305     {
2306       /* The display name of the account is not the one that we'd assign by
2307        * default; assume that the user changed it manually */
2308       g_object_set (priv->settings, "display-name-overridden", TRUE, NULL);
2309     }
2310 }
2311
2312 static void
2313 do_dispose (GObject *obj)
2314 {
2315   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2316   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2317
2318   if (priv->dispose_run)
2319     return;
2320
2321   priv->dispose_run = TRUE;
2322
2323   if (priv->settings != NULL)
2324     {
2325       TpAccount *account;
2326       account = empathy_account_settings_get_account (priv->settings);
2327
2328       if (account != NULL)
2329         {
2330           g_signal_handlers_disconnect_by_func (account,
2331               empathy_account_widget_enabled_cb, self);
2332         }
2333
2334       g_object_unref (priv->settings);
2335       priv->settings = NULL;
2336     }
2337
2338   if (priv->account_manager != NULL)
2339     {
2340       g_object_unref (priv->account_manager);
2341       priv->account_manager = NULL;
2342     }
2343
2344   if (G_OBJECT_CLASS (empathy_account_widget_parent_class)->dispose != NULL)
2345     G_OBJECT_CLASS (empathy_account_widget_parent_class)->dispose (obj);
2346 }
2347
2348 static void
2349 do_finalize (GObject *obj)
2350 {
2351   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2352   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2353
2354   g_free (self->ui_details->default_focus);
2355   g_slice_free (EmpathyAccountWidgetUIDetails, self->ui_details);
2356
2357   g_free (priv->jid_suffix);
2358
2359   if (G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize != NULL)
2360     G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize (obj);
2361 }
2362
2363 static void
2364 empathy_account_widget_class_init (EmpathyAccountWidgetClass *klass)
2365 {
2366   GObjectClass *oclass = G_OBJECT_CLASS (klass);
2367   GParamSpec *param_spec;
2368
2369   oclass->get_property = do_get_property;
2370   oclass->set_property = do_set_property;
2371   oclass->constructed = do_constructed;
2372   oclass->dispose = do_dispose;
2373   oclass->finalize = do_finalize;
2374
2375   param_spec = g_param_spec_string ("protocol",
2376       "protocol", "The protocol of the account",
2377       NULL,
2378       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2379   g_object_class_install_property (oclass, PROP_PROTOCOL, param_spec);
2380
2381   param_spec = g_param_spec_object ("settings",
2382       "settings", "The settings of the account",
2383       EMPATHY_TYPE_ACCOUNT_SETTINGS,
2384       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2385   g_object_class_install_property (oclass, PROP_SETTINGS, param_spec);
2386
2387   param_spec = g_param_spec_boolean ("simple",
2388       "simple", "Whether the account widget is a simple or an advanced one",
2389       FALSE,
2390       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2391   g_object_class_install_property (oclass, PROP_SIMPLE, param_spec);
2392
2393   param_spec = g_param_spec_boolean ("creating-account",
2394       "creating-account",
2395       "TRUE if we're creating an account, FALSE if we're modifying it",
2396       FALSE,
2397       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2398   g_object_class_install_property (oclass, PROP_CREATING_ACCOUNT, param_spec);
2399
2400   param_spec = g_param_spec_boolean ("other-accounts-exist",
2401       "other-accounts-exist",
2402       "TRUE if there are any other accounts (even if this isn't yet saved)",
2403       FALSE,
2404       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2405   g_object_class_install_property (oclass, PROP_OTHER_ACCOUNTS_EXIST,
2406                   param_spec);
2407
2408   signals[HANDLE_APPLY] =
2409     g_signal_new ("handle-apply", G_TYPE_FROM_CLASS (klass),
2410         G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2411         g_cclosure_marshal_VOID__BOOLEAN,
2412         G_TYPE_NONE,
2413         1, G_TYPE_BOOLEAN);
2414
2415   /* This signal is emitted when an account has been created and enabled. */
2416   signals[ACCOUNT_CREATED] =
2417       g_signal_new ("account-created", G_TYPE_FROM_CLASS (klass),
2418           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2419           g_cclosure_marshal_VOID__OBJECT,
2420           G_TYPE_NONE,
2421           1, G_TYPE_OBJECT);
2422
2423   signals[CANCELLED] =
2424       g_signal_new ("cancelled", G_TYPE_FROM_CLASS (klass),
2425           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2426           g_cclosure_marshal_VOID__VOID,
2427           G_TYPE_NONE,
2428           0);
2429
2430   g_type_class_add_private (klass, sizeof (EmpathyAccountWidgetPriv));
2431 }
2432
2433 static void
2434 empathy_account_widget_init (EmpathyAccountWidget *self)
2435 {
2436   EmpathyAccountWidgetPriv *priv =
2437     G_TYPE_INSTANCE_GET_PRIVATE ((self), EMPATHY_TYPE_ACCOUNT_WIDGET,
2438         EmpathyAccountWidgetPriv);
2439
2440   self->priv = priv;
2441   priv->dispose_run = FALSE;
2442
2443   self->ui_details = g_slice_new0 (EmpathyAccountWidgetUIDetails);
2444 }
2445
2446 /* public methods */
2447
2448 void
2449 empathy_account_widget_discard_pending_changes
2450     (EmpathyAccountWidget *widget)
2451 {
2452   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
2453
2454   empathy_account_settings_discard_changes (priv->settings);
2455   priv->contains_pending_changes = FALSE;
2456 }
2457
2458 gboolean
2459 empathy_account_widget_contains_pending_changes (EmpathyAccountWidget *widget)
2460 {
2461   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
2462
2463   return priv->contains_pending_changes;
2464 }
2465
2466 void
2467 empathy_account_widget_handle_params (EmpathyAccountWidget *self,
2468     const gchar *first_widget,
2469     ...)
2470 {
2471   va_list args;
2472
2473   va_start (args, first_widget);
2474   account_widget_handle_params_valist (self, first_widget, args);
2475   va_end (args);
2476 }
2477
2478 GtkWidget *
2479 empathy_account_widget_get_widget (EmpathyAccountWidget *widget)
2480 {
2481   return widget->ui_details->widget;
2482 }
2483
2484 EmpathyAccountWidget *
2485 empathy_account_widget_new_for_protocol (EmpathyAccountSettings *settings,
2486     gboolean simple)
2487 {
2488   EmpathyAccountWidget *self;
2489
2490   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), NULL);
2491
2492   self = g_object_new
2493     (EMPATHY_TYPE_ACCOUNT_WIDGET,
2494         "settings", settings, "simple", simple,
2495         "creating-account",
2496         empathy_account_settings_get_account (settings) == NULL,
2497         NULL);
2498
2499   return self;
2500 }
2501
2502 gchar *
2503 empathy_account_widget_get_default_display_name (EmpathyAccountWidget *self)
2504 {
2505   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2506   const gchar *login_id;
2507   const gchar *protocol, *p;
2508   gchar *default_display_name;
2509   Service service;
2510
2511   login_id = empathy_account_settings_get_string (priv->settings, "account");
2512   protocol = empathy_account_settings_get_protocol (priv->settings);
2513   service = account_widget_get_service (self);
2514
2515   if (login_id != NULL)
2516     {
2517       /* TODO: this should be done in empathy-account-widget-irc */
2518       if (!tp_strdiff (protocol, "irc"))
2519         {
2520           EmpathyIrcNetwork *network;
2521
2522           network = empathy_irc_network_chooser_get_network (
2523               priv->irc_network_chooser);
2524           g_assert (network != NULL);
2525
2526           /* To translators: The first parameter is the login id and the
2527            * second one is the network. The resulting string will be something
2528            * like: "MyUserName on freenode".
2529            * You should reverse the order of these arguments if the
2530            * server should come before the login id in your locale.*/
2531           default_display_name = g_strdup_printf (_("%1$s on %2$s"),
2532               login_id, empathy_irc_network_get_name (network));
2533         }
2534       else if (service == FACEBOOK_SERVICE)
2535         {
2536           gchar *tmp;
2537
2538           tmp = remove_jid_suffix (self, login_id);
2539           default_display_name = g_strdup_printf ("Facebook (%s)", tmp);
2540           g_free (tmp);
2541         }
2542       else
2543         {
2544           default_display_name = g_strdup (login_id);
2545         }
2546
2547       return default_display_name;
2548     }
2549
2550   if ((p = empathy_protocol_name_to_display_name (protocol)) != NULL)
2551     protocol = p;
2552
2553   if (protocol != NULL)
2554     {
2555       /* To translators: The parameter is the protocol name. The resulting
2556        * string will be something like: "Jabber Account" */
2557       default_display_name = g_strdup_printf (_("%s Account"), protocol);
2558     }
2559   else
2560     {
2561       default_display_name = g_strdup (_("New account"));
2562     }
2563
2564   return default_display_name;
2565 }
2566
2567 /* Used by subclass to indicate that widget contains pending changes */
2568 void
2569 empathy_account_widget_changed (EmpathyAccountWidget *self)
2570 {
2571   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2572
2573   account_widget_handle_control_buttons_sensitivity (self);
2574   priv->contains_pending_changes = TRUE;
2575 }
2576
2577 void
2578 empathy_account_widget_set_account_param (EmpathyAccountWidget *self,
2579     const gchar *account)
2580 {
2581   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2582
2583   if (priv->param_account_widget == NULL)
2584     return;
2585
2586   gtk_entry_set_text (GTK_ENTRY (priv->param_account_widget), account);
2587 }
2588
2589 void
2590 empathy_account_widget_set_password_param (EmpathyAccountWidget *self,
2591     const gchar *account)
2592 {
2593   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2594
2595   if (priv->param_password_widget == NULL)
2596     return;
2597
2598   gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), account);
2599 }