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