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