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