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