]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-account-widget.c
Updated POTFILES.in
[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 (
1198           "gnome-online-accounts-panel.desktop");
1199
1200       if (desktop_info == NULL)
1201         g_critical ("Could not locate 'gnome-online-accounts-panel.desktop'");
1202       else
1203         name = g_app_info_get_name (G_APP_INFO (desktop_info));
1204     }
1205
1206   if (name != NULL)
1207     {
1208       str = g_strdup_printf (
1209           _("The account %s is edited via %s."),
1210           empathy_account_settings_get_display_name (settings), name);
1211     }
1212   else
1213     {
1214       str = g_strdup_printf (
1215           _("The account %s cannot be edited in Empathy."),
1216           empathy_account_settings_get_display_name (settings));
1217     }
1218
1219   widget = gtk_label_new (str);
1220   gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
1221   g_free (str);
1222
1223   bar = gtk_info_bar_new ();
1224   gtk_info_bar_set_message_type (GTK_INFO_BAR (bar), GTK_MESSAGE_INFO);
1225   gtk_container_add (
1226       GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))),
1227       widget);
1228   gtk_container_set_border_width (GTK_CONTAINER (bar), 6);
1229
1230   if (!tp_strdiff (provider, "com.meego.libsocialweb"))
1231     {
1232       /* we know how to handle this external provider */
1233       widget = gtk_info_bar_add_button (GTK_INFO_BAR (bar),
1234           _("Launch My Web Accounts"), RESPONSE_LAUNCH);
1235
1236       g_signal_connect (widget, "clicked",
1237           G_CALLBACK (account_widget_launch_external_clicked_meego), account);
1238     }
1239   else if (desktop_info != NULL)
1240     {
1241       /* general handler */
1242       str = g_strdup_printf (_("Edit %s"), name);
1243
1244       widget = gtk_info_bar_add_button (GTK_INFO_BAR (bar),
1245           str, RESPONSE_LAUNCH);
1246
1247       g_object_set_data_full (G_OBJECT (widget), "app-info",
1248           g_object_ref (desktop_info), g_object_unref);
1249
1250       g_signal_connect (widget, "clicked",
1251           G_CALLBACK (account_widget_launch_external_clicked), account);
1252
1253       g_free (str);
1254     }
1255
1256   gtk_box_pack_start (GTK_BOX (self->ui_details->widget), bar,
1257       FALSE, TRUE, 0);
1258   gtk_box_pack_start (GTK_BOX (self->ui_details->widget),
1259       priv->table_common_settings, FALSE, TRUE, 0);
1260
1261   gtk_widget_show_all (self->ui_details->widget);
1262
1263   tp_clear_object (&desktop_info);
1264 }
1265
1266 static void
1267 account_widget_build_salut (EmpathyAccountWidget *self,
1268     const char *filename)
1269 {
1270   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1271   GtkWidget *expander_advanced;
1272
1273   self->ui_details->gui = empathy_builder_get_file (filename,
1274       "table_common_settings", &priv->table_common_settings,
1275       "vbox_salut_settings", &self->ui_details->widget,
1276       "expander_advanced_settings", &expander_advanced,
1277       NULL);
1278
1279   empathy_account_widget_handle_params (self,
1280       "entry_published", "published-name",
1281       "entry_nickname", "nickname",
1282       "entry_first_name", "first-name",
1283       "entry_last_name", "last-name",
1284       "entry_email", "email",
1285       "entry_jid", "jid",
1286       NULL);
1287
1288   if (priv->simple)
1289     gtk_widget_hide (expander_advanced);
1290
1291   self->ui_details->default_focus = g_strdup ("entry_first_name");
1292 }
1293
1294 static void
1295 account_widget_build_irc (EmpathyAccountWidget *self,
1296   const char *filename)
1297 {
1298   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1299
1300   empathy_account_settings_set_regex (priv->settings, "account",
1301       ACCOUNT_REGEX_IRC);
1302   empathy_account_settings_set_regex (priv->settings, "username",
1303       USERNAME_REGEX_IRC);
1304
1305   if (priv->simple)
1306     {
1307       priv->irc_network_chooser = empathy_account_widget_irc_build_simple (self,
1308           filename);
1309     }
1310   else
1311     {
1312       priv->irc_network_chooser = empathy_account_widget_irc_build (self,
1313           filename, &priv->table_common_settings);
1314     }
1315 }
1316
1317 static void
1318 account_widget_build_sip (EmpathyAccountWidget *self,
1319   const char *filename)
1320 {
1321   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1322   empathy_account_widget_sip_build (self, filename,
1323     &priv->table_common_settings);
1324
1325   if (priv->simple)
1326     {
1327       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1328               self->ui_details->gui, "remember_password_simple"));
1329     }
1330   else
1331     {
1332       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1333               self->ui_details->gui, "remember_password"));
1334     }
1335 }
1336
1337 static void
1338 account_widget_build_msn (EmpathyAccountWidget *self,
1339     const char *filename)
1340 {
1341   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1342
1343   empathy_account_settings_set_regex (priv->settings, "account",
1344       ACCOUNT_REGEX_MSN);
1345
1346   if (priv->simple)
1347     {
1348       self->ui_details->gui = empathy_builder_get_file (filename,
1349           "vbox_msn_simple", &self->ui_details->widget,
1350           NULL);
1351
1352       empathy_account_widget_handle_params (self,
1353           "entry_id_simple", "account",
1354           "entry_password_simple", "password",
1355           NULL);
1356
1357       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1358
1359       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1360               self->ui_details->gui, "remember_password_simple"));
1361     }
1362   else
1363     {
1364       self->ui_details->gui = empathy_builder_get_file (filename,
1365           "table_common_msn_settings", &priv->table_common_settings,
1366           "vbox_msn_settings", &self->ui_details->widget,
1367           NULL);
1368
1369       empathy_account_widget_handle_params (self,
1370           "entry_id", "account",
1371           "entry_password", "password",
1372           "entry_server", "server",
1373           "spinbutton_port", "port",
1374           NULL);
1375
1376       self->ui_details->default_focus = g_strdup ("entry_id");
1377
1378       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1379               self->ui_details->gui, "remember_password"));
1380     }
1381 }
1382
1383 static void
1384 suffix_id_widget_changed_cb (GtkWidget *entry,
1385     EmpathyAccountWidget *self)
1386 {
1387   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1388   const gchar *account;
1389
1390   g_assert (priv->jid_suffix != NULL);
1391
1392   account_widget_entry_changed_common (self, GTK_ENTRY (entry), FALSE);
1393
1394   account = empathy_account_settings_get_string (priv->settings, "account");
1395   if (!EMP_STR_EMPTY (account) &&
1396       !g_str_has_suffix (account, priv->jid_suffix))
1397     {
1398       gchar *tmp;
1399
1400       tmp = g_strdup_printf ("%s%s", account, priv->jid_suffix);
1401
1402       DEBUG ("Change account from '%s' to '%s'", account, tmp);
1403
1404       empathy_account_settings_set_string (priv->settings, "account", tmp);
1405       g_free (tmp);
1406     }
1407
1408   empathy_account_widget_changed (self);
1409 }
1410
1411 static gchar *
1412 remove_jid_suffix (EmpathyAccountWidget *self,
1413     const gchar *str)
1414 {
1415   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1416
1417   g_assert (priv->jid_suffix != NULL);
1418
1419   if (!g_str_has_suffix (str, priv->jid_suffix))
1420     return g_strdup (str);
1421
1422   return g_strndup (str, strlen (str) - strlen (priv->jid_suffix));
1423 }
1424
1425 static void
1426 setup_id_widget_with_suffix (EmpathyAccountWidget *self,
1427     GtkWidget *widget,
1428     const gchar *suffix)
1429 {
1430   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1431   const gchar *str = NULL;
1432
1433   g_object_set_data_full (G_OBJECT (widget), "param_name",
1434       g_strdup ("account"), g_free);
1435
1436   g_assert (priv->jid_suffix == NULL);
1437   priv->jid_suffix = g_strdup (suffix);
1438
1439   str = empathy_account_settings_get_string (priv->settings, "account");
1440   if (str != NULL)
1441     {
1442       gchar *tmp;
1443
1444       tmp = remove_jid_suffix (self, str);
1445       gtk_entry_set_text (GTK_ENTRY (widget), tmp);
1446       g_free (tmp);
1447     }
1448
1449   priv->param_account_widget = widget;
1450
1451   g_signal_connect (widget, "changed",
1452       G_CALLBACK (suffix_id_widget_changed_cb), self);
1453 }
1454
1455 static Service
1456 account_widget_get_service (EmpathyAccountWidget *self)
1457 {
1458   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1459   const gchar *icon_name, *service;
1460
1461   icon_name = empathy_account_settings_get_icon_name (priv->settings);
1462   service = empathy_account_settings_get_service (priv->settings);
1463
1464   /* Previous versions of Empathy didn't set the Service property on Facebook
1465    * and gtalk accounts, so we check using the icon name as well. */
1466   if (!tp_strdiff (icon_name, "im-google-talk") ||
1467       !tp_strdiff (service, "google-talk"))
1468     return GTALK_SERVICE;
1469
1470   if (!tp_strdiff (icon_name, "im-facebook") ||
1471       !tp_strdiff (service, "facebook"))
1472     return FACEBOOK_SERVICE;
1473
1474   return NO_SERVICE;
1475 }
1476
1477 static void
1478 account_widget_build_jabber (EmpathyAccountWidget *self,
1479     const char *filename)
1480 {
1481   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1482   GtkWidget *spinbutton_port;
1483   GtkWidget *checkbutton_ssl;
1484   GtkWidget *label_id, *label_password;
1485   GtkWidget *label_id_create, *label_password_create;
1486   GtkWidget *label_example_fb;
1487   GtkWidget *label_example;
1488   GtkWidget *expander_advanced;
1489   GtkWidget *entry_id;
1490   Service service;
1491
1492   service = account_widget_get_service (self);
1493
1494   empathy_account_settings_set_regex (priv->settings, "account",
1495       ACCOUNT_REGEX_JABBER);
1496
1497   if (priv->simple && service == NO_SERVICE)
1498     {
1499       /* Simple widget for XMPP */
1500       self->ui_details->gui = empathy_builder_get_file (filename,
1501           "vbox_jabber_simple", &self->ui_details->widget,
1502           "label_id_simple", &label_id,
1503           "label_id_create", &label_id_create,
1504           "label_password_simple", &label_password,
1505           "label_password_create", &label_password_create,
1506           NULL);
1507
1508       if (empathy_account_settings_get_boolean (priv->settings, "register"))
1509         {
1510           gtk_widget_hide (label_id);
1511           gtk_widget_hide (label_password);
1512           gtk_widget_show (label_id_create);
1513           gtk_widget_show (label_password_create);
1514         }
1515
1516       empathy_account_widget_handle_params (self,
1517           "entry_id_simple", "account",
1518           "entry_password_simple", "password",
1519           NULL);
1520
1521       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1522
1523       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1524               self->ui_details->gui, "remember_password_simple"));
1525     }
1526   else if (priv->simple && service == GTALK_SERVICE)
1527     {
1528       /* Simple widget for Google Talk */
1529       self->ui_details->gui = empathy_builder_get_file (filename,
1530           "vbox_gtalk_simple", &self->ui_details->widget,
1531           NULL);
1532
1533       empathy_account_widget_handle_params (self,
1534           "entry_id_g_simple", "account",
1535           "entry_password_g_simple", "password",
1536           NULL);
1537
1538       self->ui_details->default_focus = g_strdup ("entry_id_g_simple");
1539
1540       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1541               self->ui_details->gui, "remember_password_g_simple"));
1542     }
1543   else if (priv->simple && service == FACEBOOK_SERVICE)
1544     {
1545       /* Simple widget for Facebook */
1546       self->ui_details->gui = empathy_builder_get_file (filename,
1547           "vbox_fb_simple", &self->ui_details->widget,
1548           "entry_id_fb_simple", &entry_id,
1549           NULL);
1550
1551       empathy_account_widget_handle_params (self,
1552           "entry_password_fb_simple", "password",
1553           NULL);
1554
1555       setup_id_widget_with_suffix (self, entry_id, "@chat.facebook.com");
1556
1557       self->ui_details->default_focus = g_strdup ("entry_id_fb_simple");
1558
1559       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1560               self->ui_details->gui, "remember_password_fb_simple"));
1561     }
1562   else
1563     {
1564       ServiceInfo info = services_infos[service];
1565
1566       /* Full widget for XMPP, Google Talk and Facebook*/
1567       self->ui_details->gui = empathy_builder_get_file (filename,
1568           "table_common_settings", &priv->table_common_settings,
1569           "vbox_jabber_settings", &self->ui_details->widget,
1570           "spinbutton_port", &spinbutton_port,
1571           "checkbutton_ssl", &checkbutton_ssl,
1572           "label_username_f_example", &label_example_fb,
1573           info.label_username_example, &label_example,
1574           "expander_advanced", &expander_advanced,
1575           "entry_id", &entry_id,
1576           "label_id", &label_id,
1577           NULL);
1578
1579       empathy_account_widget_handle_params (self,
1580           "entry_password", "password",
1581           "entry_resource", "resource",
1582           "entry_server", "server",
1583           "spinbutton_port", "port",
1584           "spinbutton_priority", "priority",
1585           "checkbutton_ssl", "old-ssl",
1586           "checkbutton_ignore_ssl_errors", "ignore-ssl-errors",
1587           "checkbutton_encryption", "require-encryption",
1588           NULL);
1589
1590       if (service == FACEBOOK_SERVICE)
1591         {
1592           gtk_label_set_label (GTK_LABEL (label_id), _("Username:"));
1593
1594           /* Facebook special case the entry ID widget to hide the
1595            * "@chat.facebook.com" part */
1596           setup_id_widget_with_suffix (self, entry_id, "@chat.facebook.com");
1597         }
1598       else
1599         {
1600           empathy_account_widget_setup_widget (self, entry_id, "account");
1601         }
1602
1603       self->ui_details->default_focus = g_strdup ("entry_id");
1604       priv->spinbutton_port = spinbutton_port;
1605
1606       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1607               self->ui_details->gui, "remember_password"));
1608
1609       g_signal_connect (checkbutton_ssl, "toggled",
1610           G_CALLBACK (account_widget_jabber_ssl_toggled_cb),
1611           self);
1612
1613       if (service == FACEBOOK_SERVICE)
1614         {
1615           GtkContainer *parent;
1616           GList *children;
1617
1618           /* Removing the label from list of focusable widgets */
1619           parent = GTK_CONTAINER (gtk_widget_get_parent (label_example_fb));
1620           children = gtk_container_get_children (parent);
1621           children = g_list_remove (children, label_example_fb);
1622           gtk_container_set_focus_chain (parent, children);
1623           g_list_free (children);
1624         }
1625
1626       gtk_widget_show (label_example);
1627
1628       if (!info.show_advanced)
1629         gtk_widget_hide (expander_advanced);
1630     }
1631 }
1632
1633 static void
1634 account_widget_build_icq (EmpathyAccountWidget *self,
1635     const char *filename)
1636 {
1637   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1638   GtkWidget *spinbutton_port;
1639
1640   empathy_account_settings_set_regex (priv->settings, "account",
1641       ACCOUNT_REGEX_ICQ);
1642
1643   if (priv->simple)
1644     {
1645       self->ui_details->gui = empathy_builder_get_file (filename,
1646           "vbox_icq_simple", &self->ui_details->widget,
1647           NULL);
1648
1649       empathy_account_widget_handle_params (self,
1650           "entry_uin_simple", "account",
1651           "entry_password_simple", "password",
1652           NULL);
1653
1654       self->ui_details->default_focus = g_strdup ("entry_uin_simple");
1655
1656       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1657               self->ui_details->gui, "remember_password_simple"));
1658     }
1659   else
1660     {
1661       self->ui_details->gui = empathy_builder_get_file (filename,
1662           "table_common_settings", &priv->table_common_settings,
1663           "vbox_icq_settings", &self->ui_details->widget,
1664           "spinbutton_port", &spinbutton_port,
1665           NULL);
1666
1667       empathy_account_widget_handle_params (self,
1668           "entry_uin", "account",
1669           "entry_password", "password",
1670           "entry_server", "server",
1671           "spinbutton_port", "port",
1672           "entry_charset", "charset",
1673           NULL);
1674
1675       self->ui_details->default_focus = g_strdup ("entry_uin");
1676
1677       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1678               self->ui_details->gui, "remember_password"));
1679     }
1680 }
1681
1682 static void
1683 account_widget_build_aim (EmpathyAccountWidget *self,
1684     const char *filename)
1685 {
1686   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1687   GtkWidget *spinbutton_port;
1688
1689   if (priv->simple)
1690     {
1691       self->ui_details->gui = empathy_builder_get_file (filename,
1692           "vbox_aim_simple", &self->ui_details->widget,
1693           NULL);
1694
1695       empathy_account_widget_handle_params (self,
1696           "entry_screenname_simple", "account",
1697           "entry_password_simple", "password",
1698           NULL);
1699
1700       self->ui_details->default_focus = g_strdup ("entry_screenname_simple");
1701
1702       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1703               self->ui_details->gui, "remember_password_simple"));
1704     }
1705   else
1706     {
1707       self->ui_details->gui = empathy_builder_get_file (filename,
1708           "table_common_settings", &priv->table_common_settings,
1709           "vbox_aim_settings", &self->ui_details->widget,
1710           "spinbutton_port", &spinbutton_port,
1711           NULL);
1712
1713       empathy_account_widget_handle_params (self,
1714           "entry_screenname", "account",
1715           "entry_password", "password",
1716           "entry_server", "server",
1717           "spinbutton_port", "port",
1718           NULL);
1719
1720       self->ui_details->default_focus = g_strdup ("entry_screenname");
1721
1722       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1723               self->ui_details->gui, "remember_password"));
1724     }
1725 }
1726
1727 static void
1728 account_widget_build_yahoo (EmpathyAccountWidget *self,
1729     const char *filename)
1730 {
1731   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1732
1733   empathy_account_settings_set_regex (priv->settings, "account",
1734       ACCOUNT_REGEX_YAHOO);
1735
1736   if (priv->simple)
1737     {
1738       self->ui_details->gui = empathy_builder_get_file (filename,
1739           "vbox_yahoo_simple", &self->ui_details->widget,
1740           NULL);
1741
1742       empathy_account_widget_handle_params (self,
1743           "entry_id_simple", "account",
1744           "entry_password_simple", "password",
1745           NULL);
1746
1747       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1748
1749       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1750               self->ui_details->gui, "remember_password_simple"));
1751     }
1752   else
1753     {
1754       self->ui_details->gui = empathy_builder_get_file (filename,
1755           "table_common_settings", &priv->table_common_settings,
1756           "vbox_yahoo_settings", &self->ui_details->widget,
1757           NULL);
1758
1759       empathy_account_widget_handle_params (self,
1760           "entry_id", "account",
1761           "entry_password", "password",
1762           "entry_locale", "room-list-locale",
1763           "entry_charset", "charset",
1764           "spinbutton_port", "port",
1765           "checkbutton_ignore_invites", "ignore-invites",
1766           NULL);
1767
1768       self->ui_details->default_focus = g_strdup ("entry_id");
1769
1770       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1771               self->ui_details->gui, "remember_password"));
1772     }
1773 }
1774
1775 static void
1776 account_widget_build_groupwise (EmpathyAccountWidget *self,
1777     const char *filename)
1778 {
1779   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1780
1781   if (priv->simple)
1782     {
1783       self->ui_details->gui = empathy_builder_get_file (filename,
1784           "vbox_groupwise_simple", &self->ui_details->widget,
1785           NULL);
1786
1787       empathy_account_widget_handle_params (self,
1788           "entry_id_simple", "account",
1789           "entry_password_simple", "password",
1790           NULL);
1791
1792       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1793
1794       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1795               self->ui_details->gui, "remember_password_simple"));
1796     }
1797   else
1798     {
1799       self->ui_details->gui = empathy_builder_get_file (filename,
1800           "table_common_groupwise_settings", &priv->table_common_settings,
1801           "vbox_groupwise_settings", &self->ui_details->widget,
1802           NULL);
1803
1804       empathy_account_widget_handle_params (self,
1805           "entry_id", "account",
1806           "entry_password", "password",
1807           "entry_server", "server",
1808           "spinbutton_port", "port",
1809           NULL);
1810
1811       self->ui_details->default_focus = g_strdup ("entry_id");
1812
1813       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1814               self->ui_details->gui, "remember_password"));
1815     }
1816 }
1817
1818 static void
1819 account_widget_destroy_cb (GtkWidget *widget,
1820     EmpathyAccountWidget *self)
1821 {
1822   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1823   /* set the destroyed flag - workaround */
1824   priv->destroyed = TRUE;
1825
1826   g_object_unref (self);
1827 }
1828
1829 void
1830 empathy_account_widget_set_other_accounts_exist (EmpathyAccountWidget *self,
1831     gboolean others_exist)
1832 {
1833   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1834
1835   priv->other_accounts_exist = others_exist;
1836
1837   if (priv->creating_account)
1838     account_widget_handle_control_buttons_sensitivity (self);
1839 }
1840
1841 static void
1842 do_set_property (GObject *object,
1843     guint prop_id,
1844     const GValue *value,
1845     GParamSpec *pspec)
1846 {
1847   EmpathyAccountWidgetPriv *priv = GET_PRIV (object);
1848
1849   switch (prop_id)
1850     {
1851     case PROP_SETTINGS:
1852       priv->settings = g_value_dup_object (value);
1853       break;
1854     case PROP_SIMPLE:
1855       priv->simple = g_value_get_boolean (value);
1856       break;
1857     case PROP_CREATING_ACCOUNT:
1858       priv->creating_account = g_value_get_boolean (value);
1859       break;
1860     case PROP_OTHER_ACCOUNTS_EXIST:
1861       empathy_account_widget_set_other_accounts_exist (
1862           EMPATHY_ACCOUNT_WIDGET (object), g_value_get_boolean (value));
1863       break;
1864     default:
1865       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1866     }
1867 }
1868
1869 static void
1870 do_get_property (GObject *object,
1871     guint prop_id,
1872     GValue *value,
1873     GParamSpec *pspec)
1874 {
1875   EmpathyAccountWidgetPriv *priv = GET_PRIV (object);
1876
1877   switch (prop_id)
1878     {
1879     case PROP_PROTOCOL:
1880       g_value_set_string (value,
1881         empathy_account_settings_get_protocol (priv->settings));
1882       break;
1883     case PROP_SETTINGS:
1884       g_value_set_object (value, priv->settings);
1885       break;
1886     case PROP_SIMPLE:
1887       g_value_set_boolean (value, priv->simple);
1888       break;
1889     case PROP_CREATING_ACCOUNT:
1890       g_value_set_boolean (value, priv->creating_account);
1891       break;
1892     case PROP_OTHER_ACCOUNTS_EXIST:
1893       g_value_set_boolean (value, priv->other_accounts_exist);
1894       break;
1895     default:
1896       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1897     }
1898 }
1899
1900 static void
1901 set_apply_button (EmpathyAccountWidget *self)
1902 {
1903   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1904   GtkWidget *image;
1905
1906   /* We can't use the stock button as its accelerator ('A') clashes with the
1907    * Add button. */
1908   gtk_button_set_use_stock (GTK_BUTTON (priv->apply_button), FALSE);
1909
1910   gtk_button_set_label (GTK_BUTTON (priv->apply_button), _("A_pply"));
1911   gtk_button_set_use_underline (GTK_BUTTON (priv->apply_button), TRUE);
1912
1913   image = gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
1914   gtk_button_set_image (GTK_BUTTON (priv->apply_button), image);
1915 }
1916
1917 static void
1918 presence_changed_cb (TpAccountManager *manager,
1919     TpConnectionPresenceType state,
1920     const gchar *status,
1921     const gchar *message,
1922     EmpathyAccountWidget *self)
1923 {
1924   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1925
1926   if (priv->destroyed)
1927     return;
1928
1929   if (priv->apply_button == NULL)
1930     /* This button doesn't exist in 'simple' mode */
1931     return;
1932
1933   if (state > TP_CONNECTION_PRESENCE_TYPE_OFFLINE &&
1934       priv->creating_account)
1935     {
1936       /* We are online and creating a new account, display a Login button */
1937       GtkWidget *image;
1938
1939       gtk_button_set_use_stock (GTK_BUTTON (priv->apply_button), FALSE);
1940       gtk_button_set_label (GTK_BUTTON (priv->apply_button), _("L_og in"));
1941
1942       image = gtk_image_new_from_stock (GTK_STOCK_CONNECT,
1943           GTK_ICON_SIZE_BUTTON);
1944       gtk_button_set_image (GTK_BUTTON (priv->apply_button), image);
1945     }
1946   else
1947     {
1948       /* We are offline or modifying an existing account, display
1949        * a Save button */
1950       set_apply_button (self);
1951     }
1952 }
1953
1954 static void
1955 account_manager_ready_cb (GObject *source_object,
1956     GAsyncResult *result,
1957     gpointer user_data)
1958 {
1959   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (user_data);
1960   TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
1961   GError *error = NULL;
1962   TpConnectionPresenceType state;
1963
1964   if (!tp_proxy_prepare_finish (account_manager, result, &error))
1965     {
1966       DEBUG ("Failed to prepare account manager: %s", error->message);
1967       g_error_free (error);
1968       goto out;
1969     }
1970
1971   state = tp_account_manager_get_most_available_presence (account_manager, NULL,
1972       NULL);
1973
1974   /* simulate a presence change so the apply button will be changed
1975    * if needed */
1976   presence_changed_cb (account_manager, state, NULL, NULL, self);
1977
1978 out:
1979   g_object_unref (self);
1980 }
1981
1982 #define WIDGET(cm, proto) \
1983   { #cm, #proto, "empathy-account-widget-"#proto".ui", \
1984     account_widget_build_##proto }
1985
1986 #ifndef HAVE_MEEGO
1987 /* Meego doesn't support registration */
1988 static void
1989 add_register_buttons (EmpathyAccountWidget *self,
1990     TpAccount *account)
1991 {
1992   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1993   const TpConnectionManagerProtocol *protocol;
1994   GtkWidget *radiobutton_register;
1995   GtkWidget *vbox = self->ui_details->widget;
1996
1997   if (!priv->creating_account)
1998     return;
1999
2000   protocol = empathy_account_settings_get_tp_protocol (priv->settings);
2001   if (protocol == NULL)
2002     return;
2003
2004   if (!tp_connection_manager_protocol_can_register (protocol))
2005     return;
2006
2007   if (account_widget_get_service (self) != NO_SERVICE)
2008     return;
2009
2010   if (priv->simple)
2011     return;
2012
2013   priv->radiobutton_reuse = gtk_radio_button_new_with_label (NULL,
2014       _("This account already exists on the server"));
2015   radiobutton_register = gtk_radio_button_new_with_label (
2016       gtk_radio_button_get_group (GTK_RADIO_BUTTON (priv->radiobutton_reuse)),
2017       _("Create a new account on the server"));
2018
2019   gtk_box_pack_start (GTK_BOX (vbox), priv->radiobutton_reuse, FALSE, FALSE, 0);
2020   gtk_box_pack_start (GTK_BOX (vbox), radiobutton_register, FALSE, FALSE, 0);
2021   gtk_box_reorder_child (GTK_BOX (vbox), priv->radiobutton_reuse, 0);
2022   gtk_box_reorder_child (GTK_BOX (vbox), radiobutton_register, 1);
2023   gtk_widget_show (priv->radiobutton_reuse);
2024   gtk_widget_show (radiobutton_register);
2025 }
2026 #endif /* HAVE_MEEGO */
2027
2028 static void
2029 remember_password_toggled_cb (GtkToggleButton *button,
2030     EmpathyAccountWidget *self)
2031 {
2032   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2033
2034   if (gtk_toggle_button_get_active (button))
2035     {
2036       gtk_widget_set_sensitive (priv->param_password_widget, TRUE);
2037     }
2038   else
2039     {
2040       gtk_widget_set_sensitive (priv->param_password_widget, FALSE);
2041       gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), "");
2042       empathy_account_settings_unset (priv->settings, "password");
2043     }
2044 }
2045
2046 static void
2047 account_settings_password_retrieved_cb (GObject *object,
2048     gpointer user_data)
2049 {
2050   EmpathyAccountWidget *self = user_data;
2051   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2052   const gchar *password = empathy_account_settings_get_string (
2053       priv->settings, "password");
2054
2055   if (password != NULL)
2056     {
2057       /* We have to do this so that when we call gtk_entry_set_text,
2058        * the ::changed callback doesn't think the user made the
2059        * change. */
2060       priv->automatic_change = TRUE;
2061       gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), password);
2062       priv->automatic_change = FALSE;
2063     }
2064
2065   gtk_toggle_button_set_active (
2066       GTK_TOGGLE_BUTTON (priv->remember_password_widget),
2067       !EMP_STR_EMPTY (password));
2068 }
2069
2070 static void
2071 do_constructed (GObject *obj)
2072 {
2073   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2074   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2075   TpAccount *account;
2076   TpStorageRestrictionFlags storage_restrictions;
2077   const gchar *display_name, *default_display_name;
2078   guint i = 0;
2079   struct {
2080     const gchar *cm_name;
2081     const gchar *protocol;
2082     const char *file;
2083     void (*func)(EmpathyAccountWidget *self, const gchar *filename);
2084   } widgets [] = {
2085     { "salut", "local-xmpp", "empathy-account-widget-local-xmpp.ui",
2086         account_widget_build_salut },
2087     WIDGET (gabble, jabber),
2088     WIDGET (butterfly, msn),
2089     WIDGET (haze, icq),
2090     WIDGET (haze, aim),
2091     WIDGET (haze, yahoo),
2092     WIDGET (haze, groupwise),
2093     WIDGET (idle, irc),
2094     WIDGET (sofiasip, sip),
2095   };
2096
2097   account = empathy_account_settings_get_account (priv->settings);
2098
2099   if (account != NULL)
2100     storage_restrictions = tp_account_get_storage_restrictions (account);
2101   else
2102     storage_restrictions = 0;
2103
2104   /* Empathy can only edit accounts without the Cannot_Set_Parameters flag */
2105   if (storage_restrictions & TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS)
2106     {
2107       DEBUG ("Account is provided by an external storage provider");
2108
2109       account_widget_build_external (self, priv->settings);
2110     }
2111   else
2112     {
2113       const gchar *protocol, *cm_name;
2114
2115       cm_name = empathy_account_settings_get_cm (priv->settings);
2116       protocol = empathy_account_settings_get_protocol (priv->settings);
2117
2118       for (i = 0 ; i < G_N_ELEMENTS (widgets); i++)
2119         {
2120           if (!tp_strdiff (widgets[i].cm_name, cm_name) &&
2121               !tp_strdiff (widgets[i].protocol, protocol))
2122             {
2123               gchar *filename;
2124
2125               filename = empathy_file_lookup (widgets[i].file,
2126                   "libempathy-gtk");
2127               widgets[i].func (self, filename);
2128               g_free (filename);
2129
2130               break;
2131             }
2132         }
2133
2134       if (i == G_N_ELEMENTS (widgets))
2135         {
2136           gchar *filename = empathy_file_lookup (
2137               "empathy-account-widget-generic.ui", "libempathy-gtk");
2138           account_widget_build_generic (self, filename);
2139           g_free (filename);
2140         }
2141     }
2142
2143   /* handle default focus */
2144   if (self->ui_details->default_focus != NULL)
2145     {
2146       GObject *default_focus_entry;
2147
2148       default_focus_entry = gtk_builder_get_object
2149         (self->ui_details->gui, self->ui_details->default_focus);
2150       g_signal_connect (default_focus_entry, "realize",
2151           G_CALLBACK (gtk_widget_grab_focus),
2152           NULL);
2153     }
2154
2155   /* remember password */
2156   if (priv->param_password_widget != NULL
2157       && priv->remember_password_widget != NULL
2158       && empathy_account_settings_supports_sasl (priv->settings))
2159     {
2160       if (priv->simple)
2161         {
2162           gtk_toggle_button_set_active (
2163               GTK_TOGGLE_BUTTON (priv->remember_password_widget), TRUE);
2164         }
2165       else
2166         {
2167           gtk_toggle_button_set_active (
2168               GTK_TOGGLE_BUTTON (priv->remember_password_widget),
2169               !EMP_STR_EMPTY (empathy_account_settings_get_string (
2170                       priv->settings, "password")));
2171
2172           /* The password might not have been retrieved from the
2173            * keyring yet. We should update the remember password
2174            * toggle button and the password entry when/if it is. */
2175           tp_g_signal_connect_object (priv->settings, "password-retrieved",
2176               G_CALLBACK (account_settings_password_retrieved_cb), self, 0);
2177         }
2178
2179       g_signal_connect (priv->remember_password_widget, "toggled",
2180           G_CALLBACK (remember_password_toggled_cb), self);
2181
2182       remember_password_toggled_cb (
2183           GTK_TOGGLE_BUTTON (priv->remember_password_widget), self);
2184     }
2185   else if (priv->remember_password_widget != NULL
2186       && !empathy_account_settings_supports_sasl (priv->settings))
2187     {
2188       gtk_widget_set_visible (priv->remember_password_widget, FALSE);
2189     }
2190
2191   /* dup and init the account-manager */
2192   priv->account_manager = tp_account_manager_dup ();
2193
2194   g_object_ref (self);
2195   tp_proxy_prepare_async (priv->account_manager, NULL,
2196       account_manager_ready_cb, self);
2197
2198   /* handle apply and cancel button */
2199   if (!priv->simple &&
2200       !(storage_restrictions &
2201         TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS))
2202     {
2203       GtkWidget *hbox = gtk_hbox_new (TRUE, 3);
2204       GtkWidget *image;
2205
2206       /*  We can't use the stock button as its accelerator ('C') clashes with
2207        *  the Close button. */
2208       priv->cancel_button = gtk_button_new ();
2209       gtk_button_set_label (GTK_BUTTON (priv->cancel_button), _("Ca_ncel"));
2210       gtk_button_set_use_underline (GTK_BUTTON (priv->cancel_button), TRUE);
2211
2212       image = gtk_image_new_from_stock (GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
2213       gtk_button_set_image (GTK_BUTTON (priv->cancel_button), image);
2214
2215       priv->apply_button = gtk_button_new ();
2216       set_apply_button (self);
2217
2218       /* We'll change this button to a "Log in" one if we are creating a new
2219        * account and are connected. */
2220       tp_g_signal_connect_object (priv->account_manager,
2221           "most-available-presence-changed",
2222           G_CALLBACK (presence_changed_cb), obj, 0);
2223
2224       gtk_box_pack_end (GTK_BOX (hbox), priv->apply_button, TRUE,
2225           TRUE, 3);
2226       gtk_box_pack_end (GTK_BOX (hbox), priv->cancel_button, TRUE,
2227           TRUE, 3);
2228
2229       gtk_box_pack_end (GTK_BOX (self->ui_details->widget), hbox, FALSE,
2230           FALSE, 3);
2231
2232       g_signal_connect (priv->cancel_button, "clicked",
2233           G_CALLBACK (account_widget_cancel_clicked_cb),
2234           self);
2235       g_signal_connect (priv->apply_button, "clicked",
2236           G_CALLBACK (account_widget_apply_clicked_cb),
2237           self);
2238       gtk_widget_show_all (hbox);
2239
2240       if (priv->creating_account)
2241         /* When creating an account, the user might have nothing to enter.
2242          * That means that no control interaction might occur,
2243          * so we update the control button sensitivity manually.
2244          */
2245         account_widget_handle_control_buttons_sensitivity (self);
2246       else
2247         account_widget_set_control_buttons_sensitivity (self, FALSE);
2248     }
2249
2250 #ifndef HAVE_MEEGO
2251   add_register_buttons (self, account);
2252 #endif /* HAVE_MEEGO */
2253
2254   /* hook up to widget destruction to unref ourselves */
2255   g_signal_connect (self->ui_details->widget, "destroy",
2256       G_CALLBACK (account_widget_destroy_cb), self);
2257
2258   if (self->ui_details->gui != NULL)
2259     {
2260       empathy_builder_unref_and_keep_widget (self->ui_details->gui,
2261           self->ui_details->widget);
2262       self->ui_details->gui = NULL;
2263     }
2264
2265   display_name = empathy_account_settings_get_display_name (priv->settings);
2266   default_display_name = empathy_account_widget_get_default_display_name (self);
2267
2268   if (tp_strdiff (display_name, default_display_name) &&
2269       !priv->creating_account)
2270     {
2271       /* The display name of the account is not the one that we'd assign by
2272        * default; assume that the user changed it manually */
2273       g_object_set (priv->settings, "display-name-overridden", TRUE, NULL);
2274     }
2275 }
2276
2277 static void
2278 do_dispose (GObject *obj)
2279 {
2280   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2281   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2282
2283   if (priv->dispose_run)
2284     return;
2285
2286   priv->dispose_run = TRUE;
2287
2288   if (priv->settings != NULL)
2289     {
2290       g_object_unref (priv->settings);
2291       priv->settings = NULL;
2292     }
2293
2294   if (priv->account_manager != NULL)
2295     {
2296       g_object_unref (priv->account_manager);
2297       priv->account_manager = NULL;
2298     }
2299
2300   if (G_OBJECT_CLASS (empathy_account_widget_parent_class)->dispose != NULL)
2301     G_OBJECT_CLASS (empathy_account_widget_parent_class)->dispose (obj);
2302 }
2303
2304 static void
2305 do_finalize (GObject *obj)
2306 {
2307   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2308   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2309
2310   g_free (self->ui_details->default_focus);
2311   g_slice_free (EmpathyAccountWidgetUIDetails, self->ui_details);
2312
2313   g_free (priv->jid_suffix);
2314
2315   if (G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize != NULL)
2316     G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize (obj);
2317 }
2318
2319 static void
2320 empathy_account_widget_class_init (EmpathyAccountWidgetClass *klass)
2321 {
2322   GObjectClass *oclass = G_OBJECT_CLASS (klass);
2323   GParamSpec *param_spec;
2324
2325   oclass->get_property = do_get_property;
2326   oclass->set_property = do_set_property;
2327   oclass->constructed = do_constructed;
2328   oclass->dispose = do_dispose;
2329   oclass->finalize = do_finalize;
2330
2331   param_spec = g_param_spec_string ("protocol",
2332       "protocol", "The protocol of the account",
2333       NULL,
2334       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2335   g_object_class_install_property (oclass, PROP_PROTOCOL, param_spec);
2336
2337   param_spec = g_param_spec_object ("settings",
2338       "settings", "The settings of the account",
2339       EMPATHY_TYPE_ACCOUNT_SETTINGS,
2340       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2341   g_object_class_install_property (oclass, PROP_SETTINGS, param_spec);
2342
2343   param_spec = g_param_spec_boolean ("simple",
2344       "simple", "Whether the account widget is a simple or an advanced one",
2345       FALSE,
2346       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2347   g_object_class_install_property (oclass, PROP_SIMPLE, param_spec);
2348
2349   param_spec = g_param_spec_boolean ("creating-account",
2350       "creating-account",
2351       "TRUE if we're creating an account, FALSE if we're modifying it",
2352       FALSE,
2353       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2354   g_object_class_install_property (oclass, PROP_CREATING_ACCOUNT, param_spec);
2355
2356   param_spec = g_param_spec_boolean ("other-accounts-exist",
2357       "other-accounts-exist",
2358       "TRUE if there are any other accounts (even if this isn't yet saved)",
2359       FALSE,
2360       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2361   g_object_class_install_property (oclass, PROP_OTHER_ACCOUNTS_EXIST,
2362                   param_spec);
2363
2364   signals[HANDLE_APPLY] =
2365     g_signal_new ("handle-apply", G_TYPE_FROM_CLASS (klass),
2366         G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2367         g_cclosure_marshal_generic,
2368         G_TYPE_NONE,
2369         1, G_TYPE_BOOLEAN);
2370
2371   /* This signal is emitted when an account has been created and enabled. */
2372   signals[ACCOUNT_CREATED] =
2373       g_signal_new ("account-created", G_TYPE_FROM_CLASS (klass),
2374           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2375           g_cclosure_marshal_generic,
2376           G_TYPE_NONE,
2377           1, G_TYPE_OBJECT);
2378
2379   signals[CANCELLED] =
2380       g_signal_new ("cancelled", G_TYPE_FROM_CLASS (klass),
2381           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2382           g_cclosure_marshal_generic,
2383           G_TYPE_NONE,
2384           0);
2385
2386   g_type_class_add_private (klass, sizeof (EmpathyAccountWidgetPriv));
2387 }
2388
2389 static void
2390 empathy_account_widget_init (EmpathyAccountWidget *self)
2391 {
2392   EmpathyAccountWidgetPriv *priv =
2393     G_TYPE_INSTANCE_GET_PRIVATE ((self), EMPATHY_TYPE_ACCOUNT_WIDGET,
2394         EmpathyAccountWidgetPriv);
2395
2396   self->priv = priv;
2397   priv->dispose_run = FALSE;
2398
2399   self->ui_details = g_slice_new0 (EmpathyAccountWidgetUIDetails);
2400 }
2401
2402 /* public methods */
2403
2404 void
2405 empathy_account_widget_discard_pending_changes
2406     (EmpathyAccountWidget *widget)
2407 {
2408   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
2409
2410   empathy_account_settings_discard_changes (priv->settings);
2411   priv->contains_pending_changes = FALSE;
2412 }
2413
2414 gboolean
2415 empathy_account_widget_contains_pending_changes (EmpathyAccountWidget *widget)
2416 {
2417   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
2418
2419   return priv->contains_pending_changes;
2420 }
2421
2422 void
2423 empathy_account_widget_handle_params (EmpathyAccountWidget *self,
2424     const gchar *first_widget,
2425     ...)
2426 {
2427   va_list args;
2428
2429   va_start (args, first_widget);
2430   account_widget_handle_params_valist (self, first_widget, args);
2431   va_end (args);
2432 }
2433
2434 GtkWidget *
2435 empathy_account_widget_get_widget (EmpathyAccountWidget *widget)
2436 {
2437   return widget->ui_details->widget;
2438 }
2439
2440 EmpathyAccountWidget *
2441 empathy_account_widget_new_for_protocol (EmpathyAccountSettings *settings,
2442     gboolean simple)
2443 {
2444   EmpathyAccountWidget *self;
2445
2446   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), NULL);
2447
2448   self = g_object_new
2449     (EMPATHY_TYPE_ACCOUNT_WIDGET,
2450         "settings", settings, "simple", simple,
2451         "creating-account",
2452         empathy_account_settings_get_account (settings) == NULL,
2453         NULL);
2454
2455   return self;
2456 }
2457
2458 gchar *
2459 empathy_account_widget_get_default_display_name (EmpathyAccountWidget *self)
2460 {
2461   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2462   const gchar *login_id;
2463   const gchar *protocol, *p;
2464   gchar *default_display_name;
2465   Service service;
2466
2467   login_id = empathy_account_settings_get_string (priv->settings, "account");
2468   protocol = empathy_account_settings_get_protocol (priv->settings);
2469   service = account_widget_get_service (self);
2470
2471   if (login_id != NULL)
2472     {
2473       /* TODO: this should be done in empathy-account-widget-irc */
2474       if (!tp_strdiff (protocol, "irc"))
2475         {
2476           EmpathyIrcNetwork *network;
2477
2478           network = empathy_irc_network_chooser_get_network (
2479               priv->irc_network_chooser);
2480           g_assert (network != NULL);
2481
2482           /* To translators: The first parameter is the login id and the
2483            * second one is the network. The resulting string will be something
2484            * like: "MyUserName on freenode".
2485            * You should reverse the order of these arguments if the
2486            * server should come before the login id in your locale.*/
2487           default_display_name = g_strdup_printf (_("%1$s on %2$s"),
2488               login_id, empathy_irc_network_get_name (network));
2489         }
2490       else if (service == FACEBOOK_SERVICE)
2491         {
2492           gchar *tmp;
2493
2494           tmp = remove_jid_suffix (self, login_id);
2495           default_display_name = g_strdup_printf ("Facebook (%s)", tmp);
2496           g_free (tmp);
2497         }
2498       else
2499         {
2500           default_display_name = g_strdup (login_id);
2501         }
2502
2503       return default_display_name;
2504     }
2505
2506   if ((p = empathy_protocol_name_to_display_name (protocol)) != NULL)
2507     protocol = p;
2508
2509   if (protocol != NULL)
2510     {
2511       /* To translators: The parameter is the protocol name. The resulting
2512        * string will be something like: "Jabber Account" */
2513       default_display_name = g_strdup_printf (_("%s Account"), protocol);
2514     }
2515   else
2516     {
2517       default_display_name = g_strdup (_("New account"));
2518     }
2519
2520   return default_display_name;
2521 }
2522
2523 /* Used by subclass to indicate that widget contains pending changes */
2524 void
2525 empathy_account_widget_changed (EmpathyAccountWidget *self)
2526 {
2527   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2528
2529   account_widget_handle_control_buttons_sensitivity (self);
2530   priv->contains_pending_changes = TRUE;
2531 }
2532
2533 void
2534 empathy_account_widget_set_account_param (EmpathyAccountWidget *self,
2535     const gchar *account)
2536 {
2537   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2538
2539   if (priv->param_account_widget == NULL)
2540     return;
2541
2542   gtk_entry_set_text (GTK_ENTRY (priv->param_account_widget), account);
2543 }
2544
2545 void
2546 empathy_account_widget_set_password_param (EmpathyAccountWidget *self,
2547     const gchar *account)
2548 {
2549   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2550
2551   if (priv->param_password_widget == NULL)
2552     return;
2553
2554   gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), account);
2555 }
2556
2557 EmpathyAccountSettings *
2558 empathy_account_widget_get_settings (EmpathyAccountWidget *self)
2559 {
2560   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2561
2562   return priv->settings;
2563 }