]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-account-widget.c
fix typo in comment
[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           GtkContainer *parent;
1444           GList *children;
1445
1446           gtk_widget_hide (label_example_jabber);
1447
1448           /* Removing the label from list of focusable widgets */
1449           parent = GTK_CONTAINER (gtk_widget_get_parent (label_example_fb));
1450           children = gtk_container_get_children (parent);
1451           children = g_list_remove (children, label_example_fb);
1452           gtk_container_set_focus_chain (parent, children);
1453           g_list_free (children);
1454
1455           gtk_widget_show (label_example_fb);
1456           gtk_widget_hide (expander_advanced);
1457         }
1458     }
1459 }
1460
1461 static void
1462 account_widget_build_icq (EmpathyAccountWidget *self,
1463     const char *filename)
1464 {
1465   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1466   GtkWidget *spinbutton_port;
1467
1468   empathy_account_settings_set_regex (priv->settings, "account",
1469       ACCOUNT_REGEX_ICQ);
1470
1471   if (priv->simple)
1472     {
1473       self->ui_details->gui = empathy_builder_get_file (filename,
1474           "vbox_icq_simple", &self->ui_details->widget,
1475           NULL);
1476
1477       empathy_account_widget_handle_params (self,
1478           "entry_uin_simple", "account",
1479           "entry_password_simple", "password",
1480           NULL);
1481
1482       self->ui_details->default_focus = g_strdup ("entry_uin_simple");
1483     }
1484   else
1485     {
1486       self->ui_details->gui = empathy_builder_get_file (filename,
1487           "table_common_settings", &priv->table_common_settings,
1488           "vbox_icq_settings", &self->ui_details->widget,
1489           "spinbutton_port", &spinbutton_port,
1490           NULL);
1491
1492       empathy_account_widget_handle_params (self,
1493           "entry_uin", "account",
1494           "entry_password", "password",
1495           "entry_server", "server",
1496           "spinbutton_port", "port",
1497           "entry_charset", "charset",
1498           NULL);
1499
1500       self->ui_details->default_focus = g_strdup ("entry_uin");
1501     }
1502 }
1503
1504 static void
1505 account_widget_build_aim (EmpathyAccountWidget *self,
1506     const char *filename)
1507 {
1508   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1509   GtkWidget *spinbutton_port;
1510
1511   if (priv->simple)
1512     {
1513       self->ui_details->gui = empathy_builder_get_file (filename,
1514           "vbox_aim_simple", &self->ui_details->widget,
1515           NULL);
1516
1517       empathy_account_widget_handle_params (self,
1518           "entry_screenname_simple", "account",
1519           "entry_password_simple", "password",
1520           NULL);
1521
1522       self->ui_details->default_focus = g_strdup ("entry_screenname_simple");
1523     }
1524   else
1525     {
1526       self->ui_details->gui = empathy_builder_get_file (filename,
1527           "table_common_settings", &priv->table_common_settings,
1528           "vbox_aim_settings", &self->ui_details->widget,
1529           "spinbutton_port", &spinbutton_port,
1530           NULL);
1531
1532       empathy_account_widget_handle_params (self,
1533           "entry_screenname", "account",
1534           "entry_password", "password",
1535           "entry_server", "server",
1536           "spinbutton_port", "port",
1537           NULL);
1538
1539       self->ui_details->default_focus = g_strdup ("entry_screenname");
1540     }
1541 }
1542
1543 static void
1544 account_widget_build_yahoo (EmpathyAccountWidget *self,
1545     const char *filename)
1546 {
1547   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1548
1549   empathy_account_settings_set_regex (priv->settings, "account",
1550       ACCOUNT_REGEX_YAHOO);
1551
1552   if (priv->simple)
1553     {
1554       self->ui_details->gui = empathy_builder_get_file (filename,
1555           "vbox_yahoo_simple", &self->ui_details->widget,
1556           NULL);
1557
1558       empathy_account_widget_handle_params (self,
1559           "entry_id_simple", "account",
1560           "entry_password_simple", "password",
1561           NULL);
1562
1563       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1564     }
1565   else
1566     {
1567       self->ui_details->gui = empathy_builder_get_file (filename,
1568           "table_common_settings", &priv->table_common_settings,
1569           "vbox_yahoo_settings", &self->ui_details->widget,
1570           NULL);
1571
1572       empathy_account_widget_handle_params (self,
1573           "entry_id", "account",
1574           "entry_password", "password",
1575           "entry_server", "server",
1576           "entry_locale", "room-list-locale",
1577           "entry_charset", "charset",
1578           "spinbutton_port", "port",
1579           "checkbutton_ignore_invites", "ignore-invites",
1580           NULL);
1581
1582       self->ui_details->default_focus = g_strdup ("entry_id");
1583     }
1584 }
1585
1586 static void
1587 account_widget_build_groupwise (EmpathyAccountWidget *self,
1588     const char *filename)
1589 {
1590   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1591
1592   if (priv->simple)
1593     {
1594       self->ui_details->gui = empathy_builder_get_file (filename,
1595           "vbox_groupwise_simple", &self->ui_details->widget,
1596           NULL);
1597
1598       empathy_account_widget_handle_params (self,
1599           "entry_id_simple", "account",
1600           "entry_password_simple", "password",
1601           NULL);
1602
1603       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1604     }
1605   else
1606     {
1607       self->ui_details->gui = empathy_builder_get_file (filename,
1608           "table_common_groupwise_settings", &priv->table_common_settings,
1609           "vbox_groupwise_settings", &self->ui_details->widget,
1610           NULL);
1611
1612       empathy_account_widget_handle_params (self,
1613           "entry_id", "account",
1614           "entry_password", "password",
1615           "entry_server", "server",
1616           "spinbutton_port", "port",
1617           NULL);
1618
1619       self->ui_details->default_focus = g_strdup ("entry_id");
1620     }
1621 }
1622
1623 static void
1624 account_widget_destroy_cb (GtkWidget *widget,
1625     EmpathyAccountWidget *self)
1626 {
1627   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1628   /* set the destroyed flag - workaround */
1629   priv->destroyed = TRUE;
1630
1631   g_object_unref (self);
1632 }
1633
1634 static void
1635 empathy_account_widget_enabled_cb (TpAccount *account,
1636       GParamSpec *spec,
1637       gpointer user_data)
1638 {
1639   EmpathyAccountWidget *widget = EMPATHY_ACCOUNT_WIDGET (user_data);
1640   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
1641   gboolean enabled = tp_account_is_enabled (account);
1642
1643   if (priv->enabled_checkbox != NULL)
1644     {
1645 #ifdef HAVE_MEEGO
1646       mx_gtk_light_switch_set_active (
1647           MX_GTK_LIGHT_SWITCH (priv->enabled_checkbox),
1648           enabled);
1649 #else
1650       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->enabled_checkbox),
1651           enabled);
1652 #endif /* HAVE_MEEGO */
1653     }
1654 }
1655
1656 static void
1657 #ifdef HAVE_MEEGO
1658 account_widget_switch_flipped_cb (MxGtkLightSwitch *sw,
1659     gboolean state,
1660     gpointer user_data)
1661 #else
1662 account_widget_enabled_toggled_cb (GtkToggleButton *toggle_button,
1663     gpointer user_data)
1664 #endif /* HAVE_MEEGO */
1665 {
1666   EmpathyAccountWidgetPriv *priv = GET_PRIV (user_data);
1667   TpAccount *account;
1668 #ifndef HAVE_MEEGO
1669   gboolean state;
1670
1671   state = gtk_toggle_button_get_active (toggle_button);
1672 #endif /* HAVE_MEEGO */
1673
1674   account = empathy_account_settings_get_account (priv->settings);
1675
1676   /* Enable the account according to the value of the "Enabled" checkbox */
1677   /* workaround to keep widget alive during async call */
1678   g_object_ref (user_data);
1679   tp_account_set_enabled_async (account, state,
1680       account_widget_account_enabled_cb, user_data);
1681 }
1682
1683 void
1684 empathy_account_widget_set_other_accounts_exist (EmpathyAccountWidget *self,
1685     gboolean others_exist)
1686 {
1687   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1688
1689   priv->other_accounts_exist = others_exist;
1690
1691   if (priv->creating_account)
1692     account_widget_handle_control_buttons_sensitivity (self);
1693 }
1694
1695 static void
1696 do_set_property (GObject *object,
1697     guint prop_id,
1698     const GValue *value,
1699     GParamSpec *pspec)
1700 {
1701   EmpathyAccountWidgetPriv *priv = GET_PRIV (object);
1702
1703   switch (prop_id)
1704     {
1705     case PROP_SETTINGS:
1706       priv->settings = g_value_dup_object (value);
1707       break;
1708     case PROP_SIMPLE:
1709       priv->simple = g_value_get_boolean (value);
1710       break;
1711     case PROP_CREATING_ACCOUNT:
1712       priv->creating_account = g_value_get_boolean (value);
1713       break;
1714     case PROP_OTHER_ACCOUNTS_EXIST:
1715       empathy_account_widget_set_other_accounts_exist (
1716           EMPATHY_ACCOUNT_WIDGET (object), g_value_get_boolean (value));
1717       break;
1718     default:
1719       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1720     }
1721 }
1722
1723 static void
1724 do_get_property (GObject *object,
1725     guint prop_id,
1726     GValue *value,
1727     GParamSpec *pspec)
1728 {
1729   EmpathyAccountWidgetPriv *priv = GET_PRIV (object);
1730
1731   switch (prop_id)
1732     {
1733     case PROP_PROTOCOL:
1734       g_value_set_string (value,
1735         empathy_account_settings_get_protocol (priv->settings));
1736       break;
1737     case PROP_SETTINGS:
1738       g_value_set_object (value, priv->settings);
1739       break;
1740     case PROP_SIMPLE:
1741       g_value_set_boolean (value, priv->simple);
1742       break;
1743     case PROP_CREATING_ACCOUNT:
1744       g_value_set_boolean (value, priv->creating_account);
1745       break;
1746     case PROP_OTHER_ACCOUNTS_EXIST:
1747       g_value_set_boolean (value, priv->other_accounts_exist);
1748       break;
1749     default:
1750       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1751     }
1752 }
1753
1754 static void
1755 set_apply_button (EmpathyAccountWidget *self)
1756 {
1757   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1758   GtkWidget *image;
1759
1760   /* We can't use the stock button as its accelerator ('A') clashes with the
1761    * Add button. */
1762   gtk_button_set_use_stock (GTK_BUTTON (priv->apply_button), FALSE);
1763
1764   gtk_button_set_label (GTK_BUTTON (priv->apply_button), _("A_pply"));
1765   gtk_button_set_use_underline (GTK_BUTTON (priv->apply_button), TRUE);
1766
1767   image = gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
1768   gtk_button_set_image (GTK_BUTTON (priv->apply_button), image);
1769 }
1770
1771 static void
1772 presence_changed_cb (TpAccountManager *manager,
1773     TpConnectionPresenceType state,
1774     const gchar *status,
1775     const gchar *message,
1776     EmpathyAccountWidget *self)
1777 {
1778   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1779
1780   if (priv->destroyed)
1781     return;
1782
1783   if (priv->apply_button == NULL)
1784     /* This button doesn't exist in 'simple' mode */
1785     return;
1786
1787   if (state > TP_CONNECTION_PRESENCE_TYPE_OFFLINE &&
1788       priv->creating_account)
1789     {
1790       /* We are online and creating a new account, display a Login button */
1791       GtkWidget *image;
1792
1793       gtk_button_set_use_stock (GTK_BUTTON (priv->apply_button), FALSE);
1794       gtk_button_set_label (GTK_BUTTON (priv->apply_button), _("L_og in"));
1795
1796       image = gtk_image_new_from_stock (GTK_STOCK_CONNECT,
1797           GTK_ICON_SIZE_BUTTON);
1798       gtk_button_set_image (GTK_BUTTON (priv->apply_button), image);
1799     }
1800   else
1801     {
1802       /* We are offline or modifying an existing account, display
1803        * a Save button */
1804       set_apply_button (self);
1805     }
1806 }
1807
1808 static void
1809 account_manager_ready_cb (GObject *source_object,
1810     GAsyncResult *result,
1811     gpointer user_data)
1812 {
1813   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (user_data);
1814   TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
1815   GError *error = NULL;
1816   TpConnectionPresenceType state;
1817
1818   if (!tp_account_manager_prepare_finish (account_manager, result, &error))
1819     {
1820       DEBUG ("Failed to prepare account manager: %s", error->message);
1821       g_error_free (error);
1822       goto out;
1823     }
1824
1825   state = tp_account_manager_get_most_available_presence (account_manager, NULL,
1826       NULL);
1827
1828   /* simulate a presence change so the apply button will be changed
1829    * if needed */
1830   presence_changed_cb (account_manager, state, NULL, NULL, self);
1831
1832 out:
1833   g_object_unref (self);
1834 }
1835
1836 #define WIDGET(cm, proto) \
1837   { #cm, #proto, "empathy-account-widget-"#proto".ui", \
1838     account_widget_build_##proto }
1839
1840 static void
1841 add_enable_checkbox (EmpathyAccountWidget *self,
1842     TpAccount *account)
1843 {
1844   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1845 #ifdef HAVE_MEEGO
1846   GtkWidget *w;
1847 #else
1848   GtkWidget *vbox = self->ui_details->widget;
1849 #endif /* HAVE_MEEGO */
1850   guint nb_rows, nb_columns;
1851   gboolean is_enabled;
1852
1853   /* handle the "Enabled" checkbox. We only add it when modifying an account */
1854   if (priv->creating_account || priv->table_common_settings == NULL)
1855     return;
1856
1857   is_enabled = tp_account_is_enabled (account);
1858
1859 #ifdef HAVE_MEEGO
1860   w = gtk_label_new (_("Account:"));
1861   gtk_misc_set_alignment (GTK_MISC (w), 0, 0.5);
1862
1863   priv->enabled_checkbox = mx_gtk_light_switch_new ();
1864
1865   mx_gtk_light_switch_set_active (
1866       MX_GTK_LIGHT_SWITCH (priv->enabled_checkbox), is_enabled);
1867
1868   gtk_widget_show (w);
1869 #else
1870   priv->enabled_checkbox =
1871       gtk_check_button_new_with_mnemonic (_("_Enabled"));
1872
1873   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->enabled_checkbox),
1874       is_enabled);
1875 #endif /* HAVE_MEEGO */
1876
1877   g_object_get (priv->table_common_settings, "n-rows", &nb_rows,
1878       "n-columns", &nb_columns, NULL);
1879
1880   gtk_table_resize (GTK_TABLE (priv->table_common_settings), ++nb_rows,
1881       nb_columns);
1882
1883 #ifdef HAVE_MEEGO
1884   gtk_table_attach (GTK_TABLE (priv->table_common_settings),
1885       w,
1886       0, 1, nb_rows - 1, nb_rows,
1887       GTK_FILL, 0, 0, 0);
1888   gtk_table_attach (GTK_TABLE (priv->table_common_settings),
1889       priv->enabled_checkbox,
1890       1, nb_columns, nb_rows - 1, nb_rows,
1891       GTK_EXPAND | GTK_FILL, 0, 0, 0);
1892 #else
1893   gtk_box_pack_start (GTK_BOX (vbox), priv->enabled_checkbox, FALSE, FALSE, 0);
1894   gtk_box_reorder_child (GTK_BOX (vbox), priv->enabled_checkbox, 0);
1895 #endif /* HAVE_MEEGO */
1896
1897   gtk_widget_show (priv->enabled_checkbox);
1898
1899 #ifdef HAVE_MEEGO
1900   g_signal_connect (G_OBJECT (priv->enabled_checkbox), "switch-flipped",
1901       G_CALLBACK (account_widget_switch_flipped_cb), self);
1902 #else
1903   g_signal_connect (G_OBJECT (priv->enabled_checkbox), "toggled",
1904       G_CALLBACK (account_widget_enabled_toggled_cb), self);
1905 #endif /* HAVE_MEEGO */
1906 }
1907
1908 #ifndef HAVE_MEEGO
1909 /* Meego doesn't support registration */
1910 static void
1911 add_register_buttons (EmpathyAccountWidget *self,
1912     TpAccount *account)
1913 {
1914   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1915   const TpConnectionManagerProtocol *protocol;
1916   GtkWidget *radiobutton_register;
1917   GtkWidget *vbox = self->ui_details->widget;
1918
1919   if (!priv->creating_account)
1920     return;
1921
1922   protocol = empathy_account_settings_get_tp_protocol (priv->settings);
1923   if (protocol == NULL)
1924     return;
1925
1926   if (!tp_connection_manager_protocol_can_register (protocol))
1927     return;
1928
1929   if (account_widget_is_gtalk (self) || account_widget_is_facebook (self))
1930     return;
1931
1932   if (priv->simple)
1933     return;
1934
1935   priv->radiobutton_reuse = gtk_radio_button_new_with_label (NULL,
1936       _("This account already exists on the server"));
1937   radiobutton_register = gtk_radio_button_new_with_label (
1938       gtk_radio_button_get_group (GTK_RADIO_BUTTON (priv->radiobutton_reuse)),
1939       _("Create a new account on the server"));
1940
1941   gtk_box_pack_start (GTK_BOX (vbox), priv->radiobutton_reuse, FALSE, FALSE, 0);
1942   gtk_box_pack_start (GTK_BOX (vbox), radiobutton_register, FALSE, FALSE, 0);
1943   gtk_box_reorder_child (GTK_BOX (vbox), priv->radiobutton_reuse, 0);
1944   gtk_box_reorder_child (GTK_BOX (vbox), radiobutton_register, 1);
1945   gtk_widget_show (priv->radiobutton_reuse);
1946   gtk_widget_show (radiobutton_register);
1947 }
1948 #endif /* HAVE_MEEGO */
1949
1950 static void
1951 do_constructed (GObject *obj)
1952 {
1953   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
1954   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1955   TpAccount *account;
1956   TpStorageRestrictionFlags storage_restrictions;
1957   const gchar *display_name, *default_display_name;
1958   guint i = 0;
1959   struct {
1960     const gchar *cm_name;
1961     const gchar *protocol;
1962     const char *file;
1963     void (*func)(EmpathyAccountWidget *self, const gchar *filename);
1964   } widgets [] = {
1965     { "salut", "local-xmpp", "empathy-account-widget-local-xmpp.ui",
1966         account_widget_build_salut },
1967     WIDGET (gabble, jabber),
1968     WIDGET (butterfly, msn),
1969     WIDGET (haze, icq),
1970     WIDGET (haze, aim),
1971     WIDGET (haze, yahoo),
1972     WIDGET (haze, groupwise),
1973     WIDGET (idle, irc),
1974     WIDGET (sofiasip, sip),
1975   };
1976
1977   account = empathy_account_settings_get_account (priv->settings);
1978
1979   if (account != NULL)
1980     storage_restrictions = tp_account_get_storage_restrictions (account);
1981   else
1982     storage_restrictions = 0;
1983
1984   /* Empathy can only edit accounts without the Cannot_Set_Parameters flag */
1985   if (storage_restrictions & TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS)
1986     {
1987       DEBUG ("Account is provided by an external storage provider");
1988
1989       account_widget_build_external (self, priv->settings);
1990     }
1991   else
1992     {
1993       const gchar *protocol, *cm_name;
1994
1995       cm_name = empathy_account_settings_get_cm (priv->settings);
1996       protocol = empathy_account_settings_get_protocol (priv->settings);
1997
1998       for (i = 0 ; i < G_N_ELEMENTS (widgets); i++)
1999         {
2000           if (!tp_strdiff (widgets[i].cm_name, cm_name) &&
2001               !tp_strdiff (widgets[i].protocol, protocol))
2002             {
2003               gchar *filename;
2004
2005               filename = empathy_file_lookup (widgets[i].file,
2006                   "libempathy-gtk");
2007               widgets[i].func (self, filename);
2008               g_free (filename);
2009
2010               break;
2011             }
2012         }
2013
2014       if (i == G_N_ELEMENTS (widgets))
2015         {
2016           gchar *filename = empathy_file_lookup (
2017               "empathy-account-widget-generic.ui", "libempathy-gtk");
2018           account_widget_build_generic (self, filename);
2019           g_free (filename);
2020         }
2021     }
2022
2023   /* handle default focus */
2024   if (self->ui_details->default_focus != NULL)
2025     {
2026       GObject *default_focus_entry;
2027
2028       default_focus_entry = gtk_builder_get_object
2029         (self->ui_details->gui, self->ui_details->default_focus);
2030       g_signal_connect (default_focus_entry, "realize",
2031           G_CALLBACK (gtk_widget_grab_focus),
2032           NULL);
2033     }
2034
2035   /* dup and init the account-manager */
2036   priv->account_manager = tp_account_manager_dup ();
2037
2038   g_object_ref (self);
2039   tp_account_manager_prepare_async (priv->account_manager, NULL,
2040       account_manager_ready_cb, self);
2041
2042   /* handle apply and cancel button */
2043   if (!priv->simple &&
2044       !(storage_restrictions &
2045         TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS))
2046     {
2047       GtkWidget *hbox = gtk_hbox_new (TRUE, 3);
2048       GtkWidget *image;
2049
2050       /*  We can't use the stock button as its accelerator ('C') clashes with
2051        *  the Close button. */
2052       priv->cancel_button = gtk_button_new ();
2053       gtk_button_set_label (GTK_BUTTON (priv->cancel_button), _("Ca_ncel"));
2054       gtk_button_set_use_underline (GTK_BUTTON (priv->cancel_button), TRUE);
2055
2056       image = gtk_image_new_from_stock (GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
2057       gtk_button_set_image (GTK_BUTTON (priv->cancel_button), image);
2058
2059       priv->apply_button = gtk_button_new ();
2060       set_apply_button (self);
2061
2062       /* We'll change this button to a "Log in" one if we are creating a new
2063        * account and are connected. */
2064       tp_g_signal_connect_object (priv->account_manager,
2065           "most-available-presence-changed",
2066           G_CALLBACK (presence_changed_cb), obj, 0);
2067
2068       gtk_box_pack_end (GTK_BOX (hbox), priv->apply_button, TRUE,
2069           TRUE, 3);
2070       gtk_box_pack_end (GTK_BOX (hbox), priv->cancel_button, TRUE,
2071           TRUE, 3);
2072
2073       gtk_box_pack_end (GTK_BOX (self->ui_details->widget), hbox, FALSE,
2074           FALSE, 3);
2075
2076       g_signal_connect (priv->cancel_button, "clicked",
2077           G_CALLBACK (account_widget_cancel_clicked_cb),
2078           self);
2079       g_signal_connect (priv->apply_button, "clicked",
2080           G_CALLBACK (account_widget_apply_clicked_cb),
2081           self);
2082       gtk_widget_show_all (hbox);
2083
2084       if (priv->creating_account)
2085         /* When creating an account, the user might have nothing to enter.
2086          * That means that no control interaction might occur,
2087          * so we update the control button sensitivity manually.
2088          */
2089         account_widget_handle_control_buttons_sensitivity (self);
2090       else
2091         account_widget_set_control_buttons_sensitivity (self, FALSE);
2092     }
2093
2094   if (account != NULL)
2095     {
2096       g_signal_connect (account, "notify::enabled",
2097           G_CALLBACK (empathy_account_widget_enabled_cb), self);
2098     }
2099
2100 #ifndef HAVE_MEEGO
2101   add_register_buttons (self, account);
2102 #endif /* HAVE_MEEGO */
2103
2104   /* add the Enable checkbox to accounts that support it */
2105   if (!(storage_restrictions & TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_ENABLED))
2106     add_enable_checkbox (self, account);
2107
2108   /* hook up to widget destruction to unref ourselves */
2109   g_signal_connect (self->ui_details->widget, "destroy",
2110       G_CALLBACK (account_widget_destroy_cb), self);
2111
2112   if (self->ui_details->gui != NULL)
2113     {
2114       empathy_builder_unref_and_keep_widget (self->ui_details->gui,
2115           self->ui_details->widget);
2116       self->ui_details->gui = NULL;
2117     }
2118
2119   display_name = empathy_account_settings_get_display_name (priv->settings);
2120   default_display_name = empathy_account_widget_get_default_display_name (self);
2121
2122   if (tp_strdiff (display_name, default_display_name) &&
2123       !priv->creating_account)
2124     {
2125       /* The display name of the account is not the one that we'd assign by
2126        * default; assume that the user changed it manually */
2127       g_object_set (priv->settings, "display-name-overridden", TRUE, NULL);
2128     }
2129 }
2130
2131 static void
2132 do_dispose (GObject *obj)
2133 {
2134   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2135   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2136
2137   if (priv->dispose_run)
2138     return;
2139
2140   priv->dispose_run = TRUE;
2141
2142   if (priv->settings != NULL)
2143     {
2144       TpAccount *account;
2145       account = empathy_account_settings_get_account (priv->settings);
2146
2147       if (account != NULL)
2148         {
2149           g_signal_handlers_disconnect_by_func (account,
2150               empathy_account_widget_enabled_cb, self);
2151         }
2152
2153       g_object_unref (priv->settings);
2154       priv->settings = NULL;
2155     }
2156
2157   if (priv->account_manager != NULL)
2158     {
2159       g_object_unref (priv->account_manager);
2160       priv->account_manager = NULL;
2161     }
2162
2163   if (G_OBJECT_CLASS (empathy_account_widget_parent_class)->dispose != NULL)
2164     G_OBJECT_CLASS (empathy_account_widget_parent_class)->dispose (obj);
2165 }
2166
2167 static void
2168 do_finalize (GObject *obj)
2169 {
2170   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2171
2172   g_free (self->ui_details->default_focus);
2173   g_slice_free (EmpathyAccountWidgetUIDetails, self->ui_details);
2174
2175   if (G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize != NULL)
2176     G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize (obj);
2177 }
2178
2179 static void
2180 empathy_account_widget_class_init (EmpathyAccountWidgetClass *klass)
2181 {
2182   GObjectClass *oclass = G_OBJECT_CLASS (klass);
2183   GParamSpec *param_spec;
2184
2185   oclass->get_property = do_get_property;
2186   oclass->set_property = do_set_property;
2187   oclass->constructed = do_constructed;
2188   oclass->dispose = do_dispose;
2189   oclass->finalize = do_finalize;
2190
2191   param_spec = g_param_spec_string ("protocol",
2192       "protocol", "The protocol of the account",
2193       NULL,
2194       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2195   g_object_class_install_property (oclass, PROP_PROTOCOL, param_spec);
2196
2197   param_spec = g_param_spec_object ("settings",
2198       "settings", "The settings of the account",
2199       EMPATHY_TYPE_ACCOUNT_SETTINGS,
2200       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2201   g_object_class_install_property (oclass, PROP_SETTINGS, param_spec);
2202
2203   param_spec = g_param_spec_boolean ("simple",
2204       "simple", "Whether the account widget is a simple or an advanced one",
2205       FALSE,
2206       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2207   g_object_class_install_property (oclass, PROP_SIMPLE, param_spec);
2208
2209   param_spec = g_param_spec_boolean ("creating-account",
2210       "creating-account",
2211       "TRUE if we're creating an account, FALSE if we're modifying it",
2212       FALSE,
2213       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2214   g_object_class_install_property (oclass, PROP_CREATING_ACCOUNT, param_spec);
2215
2216   param_spec = g_param_spec_boolean ("other-accounts-exist",
2217       "other-accounts-exist",
2218       "TRUE if there are any other accounts (even if this isn't yet saved)",
2219       FALSE,
2220       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2221   g_object_class_install_property (oclass, PROP_OTHER_ACCOUNTS_EXIST,
2222                   param_spec);
2223
2224   signals[HANDLE_APPLY] =
2225     g_signal_new ("handle-apply", G_TYPE_FROM_CLASS (klass),
2226         G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2227         g_cclosure_marshal_VOID__BOOLEAN,
2228         G_TYPE_NONE,
2229         1, G_TYPE_BOOLEAN);
2230
2231   /* This signal is emitted when an account has been created and enabled. */
2232   signals[ACCOUNT_CREATED] =
2233       g_signal_new ("account-created", G_TYPE_FROM_CLASS (klass),
2234           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2235           g_cclosure_marshal_VOID__OBJECT,
2236           G_TYPE_NONE,
2237           1, G_TYPE_OBJECT);
2238
2239   signals[CANCELLED] =
2240       g_signal_new ("cancelled", G_TYPE_FROM_CLASS (klass),
2241           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2242           g_cclosure_marshal_VOID__VOID,
2243           G_TYPE_NONE,
2244           0);
2245
2246   g_type_class_add_private (klass, sizeof (EmpathyAccountWidgetPriv));
2247 }
2248
2249 static void
2250 empathy_account_widget_init (EmpathyAccountWidget *self)
2251 {
2252   EmpathyAccountWidgetPriv *priv =
2253     G_TYPE_INSTANCE_GET_PRIVATE ((self), EMPATHY_TYPE_ACCOUNT_WIDGET,
2254         EmpathyAccountWidgetPriv);
2255
2256   self->priv = priv;
2257   priv->dispose_run = FALSE;
2258
2259   self->ui_details = g_slice_new0 (EmpathyAccountWidgetUIDetails);
2260 }
2261
2262 /* public methods */
2263
2264 void
2265 empathy_account_widget_discard_pending_changes
2266     (EmpathyAccountWidget *widget)
2267 {
2268   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
2269
2270   empathy_account_settings_discard_changes (priv->settings);
2271   priv->contains_pending_changes = FALSE;
2272 }
2273
2274 gboolean
2275 empathy_account_widget_contains_pending_changes (EmpathyAccountWidget *widget)
2276 {
2277   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
2278
2279   return priv->contains_pending_changes;
2280 }
2281
2282 void
2283 empathy_account_widget_handle_params (EmpathyAccountWidget *self,
2284     const gchar *first_widget,
2285     ...)
2286 {
2287   va_list args;
2288
2289   va_start (args, first_widget);
2290   account_widget_handle_params_valist (self, first_widget, args);
2291   va_end (args);
2292 }
2293
2294 GtkWidget *
2295 empathy_account_widget_get_widget (EmpathyAccountWidget *widget)
2296 {
2297   return widget->ui_details->widget;
2298 }
2299
2300 EmpathyAccountWidget *
2301 empathy_account_widget_new_for_protocol (EmpathyAccountSettings *settings,
2302     gboolean simple)
2303 {
2304   EmpathyAccountWidget *self;
2305
2306   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), NULL);
2307
2308   self = g_object_new
2309     (EMPATHY_TYPE_ACCOUNT_WIDGET,
2310         "settings", settings, "simple", simple,
2311         "creating-account",
2312         empathy_account_settings_get_account (settings) == NULL,
2313         NULL);
2314
2315   return self;
2316 }
2317
2318 gchar *
2319 empathy_account_widget_get_default_display_name (EmpathyAccountWidget *self)
2320 {
2321   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2322   const gchar *login_id;
2323   const gchar *protocol, *p;
2324   gchar *default_display_name;
2325
2326   login_id = empathy_account_settings_get_string (priv->settings, "account");
2327   protocol = empathy_account_settings_get_protocol (priv->settings);
2328
2329   if (login_id != NULL)
2330     {
2331       /* TODO: this should be done in empathy-account-widget-irc */
2332       if (!tp_strdiff (protocol, "irc"))
2333         {
2334           EmpathyIrcNetwork *network;
2335
2336           network = empathy_irc_network_chooser_get_network (
2337               priv->irc_network_chooser);
2338           g_assert (network != NULL);
2339
2340           /* To translators: The first parameter is the login id and the
2341            * second one is the network. The resulting string will be something
2342            * like: "MyUserName on freenode".
2343            * You should reverse the order of these arguments if the
2344            * server should come before the login id in your locale.*/
2345           default_display_name = g_strdup_printf (_("%1$s on %2$s"),
2346               login_id, empathy_irc_network_get_name (network));
2347         }
2348       else if (account_widget_is_facebook (self))
2349         {
2350           gchar *tmp;
2351
2352           tmp = remove_facebook_suffix (login_id);
2353           default_display_name = g_strdup_printf ("Facebook (%s)", tmp);
2354           g_free (tmp);
2355         }
2356       else
2357         {
2358           default_display_name = g_strdup (login_id);
2359         }
2360
2361       return default_display_name;
2362     }
2363
2364   if ((p = empathy_protocol_name_to_display_name (protocol)) != NULL)
2365     protocol = p;
2366
2367   if (protocol != NULL)
2368     {
2369       /* To translators: The parameter is the protocol name. The resulting
2370        * string will be something like: "Jabber Account" */
2371       default_display_name = g_strdup_printf (_("%s Account"), protocol);
2372     }
2373   else
2374     {
2375       default_display_name = g_strdup (_("New account"));
2376     }
2377
2378   return default_display_name;
2379 }
2380
2381 /* Used by subclass to indicate that widget contains pending changes */
2382 void
2383 empathy_account_widget_changed (EmpathyAccountWidget *self)
2384 {
2385   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2386
2387   account_widget_handle_control_buttons_sensitivity (self);
2388   priv->contains_pending_changes = TRUE;
2389 }
2390
2391 void
2392 empathy_account_widget_set_account_param (EmpathyAccountWidget *self,
2393     const gchar *account)
2394 {
2395   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2396
2397   if (priv->param_account_widget == NULL)
2398     return;
2399
2400   gtk_entry_set_text (GTK_ENTRY (priv->param_account_widget), account);
2401 }
2402
2403 void
2404 empathy_account_widget_set_password_param (EmpathyAccountWidget *self,
2405     const gchar *account)
2406 {
2407   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2408
2409   if (priv->param_password_widget == NULL)
2410     return;
2411
2412   gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), account);
2413 }