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