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