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