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