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