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