]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-account-widget.c
use gtk_box_new() instead of gtk_[h,v]box_new()
[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 *grid_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 *grid_common_settings,
739     GtkWidget *grid_advanced_settings)
740 {
741   TpConnectionManagerParam *params, *param;
742   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
743   guint row_common = 0, row_advanced = 0;
744
745   params = empathy_account_settings_get_tp_params (priv->settings);
746
747   for (param = params; param != NULL && param->name != NULL; param++)
748     {
749       GtkWidget       *grid_settings;
750       guint           row;
751       GtkWidget       *widget = NULL;
752       gchar           *param_name_formatted;
753
754       if (param->flags & TP_CONN_MGR_PARAM_FLAG_REQUIRED)
755         {
756           grid_settings = grid_common_settings;
757           row = row_common++;
758         }
759       else if (priv->simple)
760         {
761           return;
762         }
763       else
764         {
765           grid_settings = grid_advanced_settings;
766           row = row_advanced++;
767         }
768
769       param_name_formatted = account_widget_generic_format_param_name
770         (param->name);
771
772       if (param->dbus_signature[0] == 's')
773         {
774           gchar *str;
775
776           str = g_strdup_printf (_("%s:"), param_name_formatted);
777           widget = gtk_label_new (str);
778           gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
779           g_free (str);
780
781           gtk_grid_attach (GTK_GRID (grid_settings),
782               widget, 0, row, 1, 1);
783
784           gtk_widget_show (widget);
785
786           widget = gtk_entry_new ();
787           if (strcmp (param->name, "account") == 0)
788             {
789               g_signal_connect (widget, "realize",
790                   G_CALLBACK (gtk_widget_grab_focus),
791                   NULL);
792             }
793
794           gtk_grid_attach (GTK_GRID (grid_settings),
795               widget, 1, row, 1, 1);
796
797           gtk_widget_show (widget);
798         }
799       /* int types: ynqiuxt. double type is 'd' */
800       else if (param->dbus_signature[0] == 'y' ||
801           param->dbus_signature[0] == 'n' ||
802           param->dbus_signature[0] == 'q' ||
803           param->dbus_signature[0] == 'i' ||
804           param->dbus_signature[0] == 'u' ||
805           param->dbus_signature[0] == 'x' ||
806           param->dbus_signature[0] == 't' ||
807           param->dbus_signature[0] == 'd')
808         {
809           gchar   *str = NULL;
810           gdouble  minint = 0;
811           gdouble  maxint = 0;
812           gdouble  step = 1;
813
814           switch (param->dbus_signature[0])
815             {
816             case 'y': minint = G_MININT8;  maxint = G_MAXINT8;   break;
817             case 'n': minint = G_MININT16; maxint = G_MAXINT16;  break;
818             case 'q': minint = 0;          maxint = G_MAXUINT16; break;
819             case 'i': minint = G_MININT32; maxint = G_MAXINT32;  break;
820             case 'u': minint = 0;          maxint = G_MAXUINT32; break;
821             case 'x': minint = G_MININT64; maxint = G_MAXINT64;  break;
822             case 't': minint = 0;          maxint = G_MAXUINT64; break;
823             case 'd': minint = G_MININT32; maxint = G_MAXINT32;
824               step = 0.1; break;
825             default: g_assert_not_reached ();
826             }
827
828           str = g_strdup_printf (_("%s:"), param_name_formatted);
829           widget = gtk_label_new (str);
830           gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
831           g_free (str);
832
833           gtk_grid_attach (GTK_GRID (grid_settings),
834               widget, 0, row, 1, 1);
835           gtk_widget_show (widget);
836
837           widget = gtk_spin_button_new_with_range (minint, maxint, step);
838           gtk_grid_attach (GTK_GRID (grid_settings),
839               widget, 1, row, 1, 1);
840           gtk_widget_show (widget);
841         }
842       else if (param->dbus_signature[0] == 'b')
843         {
844           widget = gtk_check_button_new_with_label (param_name_formatted);
845           gtk_grid_attach (GTK_GRID (grid_settings),
846               widget, 0, row, 2, 1);
847           gtk_widget_show (widget);
848         }
849       else
850         {
851           DEBUG ("Unknown signature for param %s: %s",
852               param_name_formatted, param->dbus_signature);
853         }
854
855       if (widget)
856         empathy_account_widget_setup_widget (self, widget, param->name);
857
858       g_free (param_name_formatted);
859     }
860 }
861
862 static void
863 account_widget_handle_params_valist (EmpathyAccountWidget *self,
864     const gchar *first_widget,
865     va_list args)
866 {
867   GObject *object;
868   const gchar *name;
869
870   for (name = first_widget; name; name = va_arg (args, const gchar *))
871     {
872       const gchar *param_name;
873
874       param_name = va_arg (args, const gchar *);
875       object = gtk_builder_get_object (self->ui_details->gui, name);
876
877       if (!object)
878         {
879           g_warning ("Builder is missing object '%s'.", name);
880           continue;
881         }
882
883       empathy_account_widget_setup_widget (self, GTK_WIDGET (object),
884           param_name);
885     }
886 }
887
888 static void
889 account_widget_cancel_clicked_cb (GtkWidget *button,
890     EmpathyAccountWidget *self)
891 {
892   g_signal_emit (self, signals[CANCELLED], 0);
893 }
894
895 static void
896 account_widget_account_enabled_cb (GObject *source_object,
897     GAsyncResult *res,
898     gpointer user_data)
899 {
900   GError *error = NULL;
901   TpAccount *account = TP_ACCOUNT (source_object);
902   EmpathyAccountWidget *widget = EMPATHY_ACCOUNT_WIDGET (user_data);
903   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
904
905   tp_account_set_enabled_finish (account, res, &error);
906
907   if (error != NULL)
908     {
909       DEBUG ("Could not enable the account: %s", error->message);
910       g_error_free (error);
911     }
912   else
913     {
914       empathy_connect_new_account (account, priv->account_manager);
915     }
916
917   /* unref widget - part of the workaround */
918   g_object_unref (widget);
919 }
920
921 static void
922 account_widget_applied_cb (GObject *source_object,
923     GAsyncResult *res,
924     gpointer user_data)
925 {
926   GError *error = NULL;
927   TpAccount *account;
928   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (source_object);
929   EmpathyAccountWidget *widget = EMPATHY_ACCOUNT_WIDGET (user_data);
930   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
931   gboolean reconnect_required;
932
933   empathy_account_settings_apply_finish (settings, res, &reconnect_required,
934       &error);
935
936   if (error != NULL)
937     {
938       DEBUG ("Could not apply changes to account: %s", error->message);
939       g_error_free (error);
940       return;
941     }
942
943   account = empathy_account_settings_get_account (priv->settings);
944
945   if (account != NULL)
946     {
947       if (priv->creating_account)
948         {
949           /* By default, when an account is created, we enable it. */
950
951           /* workaround to keep widget alive during async call */
952           g_object_ref (widget);
953
954           tp_account_set_enabled_async (account, TRUE,
955               account_widget_account_enabled_cb, widget);
956           g_signal_emit (widget, signals[ACCOUNT_CREATED], 0, account);
957         }
958       else
959         {
960           /* If the account was offline, we always want to try reconnecting,
961            * to give it a chance to connect if the previous params were wrong.
962            * tp_account_reconnect_async() won't do anything if the requested
963            * presence is offline anyway. */
964           if (tp_account_get_connection_status (account, NULL) ==
965               TP_CONNECTION_STATUS_DISCONNECTED)
966             reconnect_required = TRUE;
967
968           if (reconnect_required && tp_account_is_enabled (account)
969               && tp_account_is_enabled (account))
970             {
971               /* After having applied changes to a user account, we
972                * reconnect it if needed. This is done so the new
973                * information entered by the user is validated on the server. */
974               tp_account_reconnect_async (account, NULL, NULL);
975             }
976         }
977     }
978
979   if (!priv->destroyed)
980     account_widget_set_control_buttons_sensitivity (widget, FALSE);
981
982   priv->contains_pending_changes = FALSE;
983
984   /* unref the widget - part of the workaround */
985   g_object_unref (widget);
986 }
987
988 static void
989 account_widget_apply_and_log_in (EmpathyAccountWidget *self)
990 {
991   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
992   gboolean display_name_overridden;
993
994   if (priv->radiobutton_reuse != NULL)
995     {
996       gboolean reuse = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (
997             priv->radiobutton_reuse));
998
999       DEBUG ("Set register param: %d", !reuse);
1000       empathy_account_settings_set_boolean (priv->settings, "register", !reuse);
1001     }
1002
1003   g_object_get (priv->settings,
1004       "display-name-overridden", &display_name_overridden, NULL);
1005
1006   if (priv->creating_account || !display_name_overridden)
1007     {
1008       gchar *display_name;
1009
1010       /* set default display name for new accounts or update if user didn't
1011        * manually override it. */
1012       display_name = empathy_account_widget_get_default_display_name (self);
1013
1014       empathy_account_settings_set_display_name_async (priv->settings,
1015           display_name, NULL, NULL);
1016
1017       g_free (display_name);
1018     }
1019
1020   /* workaround to keep widget alive during async call */
1021   g_object_ref (self);
1022   empathy_account_settings_apply_async (priv->settings,
1023       account_widget_applied_cb, self);
1024 }
1025
1026 static void
1027 account_widget_apply_clicked_cb (GtkWidget *button,
1028     EmpathyAccountWidget *self)
1029 {
1030     account_widget_apply_and_log_in (self);
1031 }
1032
1033 static void
1034 account_widget_setup_generic (EmpathyAccountWidget *self)
1035 {
1036   GtkWidget *grid_common_settings;
1037   GtkWidget *grid_advanced_settings;
1038
1039   grid_common_settings = GTK_WIDGET (gtk_builder_get_object
1040       (self->ui_details->gui, "grid_common_settings"));
1041   grid_advanced_settings = GTK_WIDGET (gtk_builder_get_object
1042       (self->ui_details->gui, "grid_advanced_settings"));
1043
1044   accounts_widget_generic_setup (self, grid_common_settings,
1045       grid_advanced_settings);
1046
1047   g_object_unref (self->ui_details->gui);
1048 }
1049
1050 static void
1051 account_widget_settings_ready_cb (EmpathyAccountSettings *settings,
1052     GParamSpec *pspec,
1053     gpointer user_data)
1054 {
1055   EmpathyAccountWidget *self = user_data;
1056   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1057
1058   if (empathy_account_settings_is_ready (priv->settings))
1059     account_widget_setup_generic (self);
1060 }
1061
1062 static void
1063 account_widget_build_generic (EmpathyAccountWidget *self,
1064     const char *filename)
1065 {
1066   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1067   GtkWidget *expander_advanced;
1068
1069   self->ui_details->gui = empathy_builder_get_file (filename,
1070       "grid_common_settings", &priv->grid_common_settings,
1071       "vbox_generic_settings", &self->ui_details->widget,
1072       "expander_advanced_settings", &expander_advanced,
1073       NULL);
1074
1075   if (priv->simple)
1076     gtk_widget_hide (expander_advanced);
1077
1078   g_object_ref (self->ui_details->gui);
1079
1080   if (empathy_account_settings_is_ready (priv->settings))
1081     account_widget_setup_generic (self);
1082   else
1083     g_signal_connect (priv->settings, "notify::ready",
1084         G_CALLBACK (account_widget_settings_ready_cb), self);
1085 }
1086
1087 static void
1088 account_widget_launch_external_clicked (GtkWidget *button,
1089     TpAccount *account)
1090 {
1091   GdkAppLaunchContext *context = NULL;
1092   GdkDisplay *display;
1093   GAppInfo *app_info;
1094   GError *error = NULL;
1095
1096   app_info = g_object_get_data (G_OBJECT (button), "app-info");
1097
1098   g_return_if_fail (G_IS_APP_INFO (app_info));
1099
1100   display = gdk_display_get_default ();
1101   context = gdk_display_get_app_launch_context (display);
1102
1103   if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context,
1104         &error))
1105     {
1106       g_critical ("Failed to bisho: %s", error->message);
1107       g_clear_error (&error);
1108     }
1109 }
1110
1111 static void
1112 account_widget_launch_external_clicked_meego (GtkWidget *button,
1113     TpAccount *account)
1114 {
1115   if (!tp_strdiff (tp_account_get_storage_provider (account),
1116         "com.meego.libsocialweb"))
1117     {
1118       /* we know how to handle this external provider */
1119       GDesktopAppInfo *desktop_info;
1120       GError *error = NULL;
1121       GdkAppLaunchContext *context = NULL;
1122       GdkDisplay *display;
1123       gchar *cmd;
1124       GAppInfo *app_info;
1125
1126       desktop_info = g_desktop_app_info_new ("gnome-control-center.desktop");
1127       if (desktop_info == NULL)
1128         {
1129           g_critical ("Could not locate 'gnome-control-center.desktop'");
1130           return;
1131         }
1132
1133       /* glib doesn't have API to start a desktop file with args... (#637875) */
1134       cmd = g_strdup_printf ("%s bisho.desktop", g_app_info_get_commandline (
1135             (GAppInfo *) desktop_info));
1136
1137       app_info = g_app_info_create_from_commandline (cmd, NULL, 0, &error);
1138       g_free (cmd);
1139
1140       if (app_info == NULL)
1141         {
1142           DEBUG ("Failed to create app info: %s", error->message);
1143           g_error_free (error);
1144           goto out;
1145         }
1146
1147       display = gdk_display_get_default ();
1148       context = gdk_display_get_app_launch_context (display);
1149
1150       if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context,
1151             &error))
1152         {
1153           g_critical ("Failed to bisho: %s", error->message);
1154           g_clear_error (&error);
1155         }
1156
1157 out:
1158       g_object_unref (desktop_info);
1159       tp_clear_object (&app_info);
1160       tp_clear_object (&context);
1161     }
1162 }
1163
1164 static void
1165 account_widget_build_external (EmpathyAccountWidget *self,
1166     EmpathyAccountSettings *settings)
1167 {
1168   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1169   TpAccount *account = empathy_account_settings_get_account (settings);
1170   GtkWidget *bar, *widget;
1171   gchar *str;
1172   const gchar *provider, *name = NULL;
1173   GDesktopAppInfo *desktop_info = NULL;
1174
1175   self->ui_details->widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
1176   priv->grid_common_settings = gtk_grid_new ();
1177
1178   provider = tp_account_get_storage_provider (account);
1179
1180   if (!tp_strdiff (provider, "com.meego.libsocialweb"))
1181     {
1182       name = _("My Web Accounts");
1183     }
1184   else if (!tp_strdiff (provider, "org.gnome.OnlineAccounts"))
1185     {
1186       /* FIXME: we should publish the .desktop file in some general way */
1187       desktop_info = g_desktop_app_info_new (
1188           "gnome-online-accounts-panel.desktop");
1189
1190       if (desktop_info == NULL)
1191         g_critical ("Could not locate 'gnome-online-accounts-panel.desktop'");
1192       else
1193         name = g_app_info_get_name (G_APP_INFO (desktop_info));
1194     }
1195
1196   if (name != NULL)
1197     {
1198       str = g_strdup_printf (
1199           _("The account %s is edited via %s."),
1200           empathy_account_settings_get_display_name (settings), name);
1201     }
1202   else
1203     {
1204       str = g_strdup_printf (
1205           _("The account %s cannot be edited in Empathy."),
1206           empathy_account_settings_get_display_name (settings));
1207     }
1208
1209   widget = gtk_label_new (str);
1210   gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
1211   g_free (str);
1212
1213   bar = gtk_info_bar_new ();
1214   gtk_info_bar_set_message_type (GTK_INFO_BAR (bar), GTK_MESSAGE_INFO);
1215   gtk_container_add (
1216       GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))),
1217       widget);
1218   gtk_container_set_border_width (GTK_CONTAINER (bar), 6);
1219
1220   if (!tp_strdiff (provider, "com.meego.libsocialweb"))
1221     {
1222       /* we know how to handle this external provider */
1223       widget = gtk_info_bar_add_button (GTK_INFO_BAR (bar),
1224           _("Launch My Web Accounts"), RESPONSE_LAUNCH);
1225
1226       g_signal_connect (widget, "clicked",
1227           G_CALLBACK (account_widget_launch_external_clicked_meego), account);
1228     }
1229   else if (desktop_info != NULL)
1230     {
1231       /* general handler */
1232       str = g_strdup_printf (_("Edit %s"), name);
1233
1234       widget = gtk_info_bar_add_button (GTK_INFO_BAR (bar),
1235           str, RESPONSE_LAUNCH);
1236
1237       g_object_set_data_full (G_OBJECT (widget), "app-info",
1238           g_object_ref (desktop_info), g_object_unref);
1239
1240       g_signal_connect (widget, "clicked",
1241           G_CALLBACK (account_widget_launch_external_clicked), account);
1242
1243       g_free (str);
1244     }
1245
1246   gtk_box_pack_start (GTK_BOX (self->ui_details->widget), bar,
1247       FALSE, TRUE, 0);
1248   gtk_box_pack_start (GTK_BOX (self->ui_details->widget),
1249       priv->grid_common_settings, FALSE, TRUE, 0);
1250
1251   gtk_widget_show_all (self->ui_details->widget);
1252
1253   tp_clear_object (&desktop_info);
1254 }
1255
1256 static void
1257 account_widget_build_salut (EmpathyAccountWidget *self,
1258     const char *filename)
1259 {
1260   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1261   GtkWidget *expander_advanced;
1262
1263   self->ui_details->gui = empathy_builder_get_file (filename,
1264       "grid_common_settings", &priv->grid_common_settings,
1265       "vbox_salut_settings", &self->ui_details->widget,
1266       "expander_advanced_settings", &expander_advanced,
1267       NULL);
1268
1269   empathy_account_widget_handle_params (self,
1270       "entry_published", "published-name",
1271       "entry_nickname", "nickname",
1272       "entry_first_name", "first-name",
1273       "entry_last_name", "last-name",
1274       "entry_email", "email",
1275       "entry_jid", "jid",
1276       NULL);
1277
1278   if (priv->simple)
1279     gtk_widget_hide (expander_advanced);
1280
1281   self->ui_details->default_focus = g_strdup ("entry_first_name");
1282 }
1283
1284 static void
1285 account_widget_build_irc (EmpathyAccountWidget *self,
1286   const char *filename)
1287 {
1288   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1289
1290   empathy_account_settings_set_regex (priv->settings, "account",
1291       ACCOUNT_REGEX_IRC);
1292   empathy_account_settings_set_regex (priv->settings, "username",
1293       USERNAME_REGEX_IRC);
1294
1295   if (priv->simple)
1296     {
1297       priv->irc_network_chooser = empathy_account_widget_irc_build_simple (self,
1298           filename);
1299     }
1300   else
1301     {
1302       priv->irc_network_chooser = empathy_account_widget_irc_build (self,
1303           filename, &priv->grid_common_settings);
1304     }
1305 }
1306
1307 static void
1308 account_widget_build_sip (EmpathyAccountWidget *self,
1309   const char *filename)
1310 {
1311   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1312   empathy_account_widget_sip_build (self, filename,
1313     &priv->grid_common_settings);
1314
1315   if (priv->simple)
1316     {
1317       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1318               self->ui_details->gui, "remember_password_simple"));
1319     }
1320   else
1321     {
1322       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1323               self->ui_details->gui, "remember_password"));
1324     }
1325 }
1326
1327 static void
1328 account_widget_build_msn (EmpathyAccountWidget *self,
1329     const char *filename)
1330 {
1331   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1332
1333   empathy_account_settings_set_regex (priv->settings, "account",
1334       ACCOUNT_REGEX_MSN);
1335
1336   if (priv->simple)
1337     {
1338       self->ui_details->gui = empathy_builder_get_file (filename,
1339           "vbox_msn_simple", &self->ui_details->widget,
1340           NULL);
1341
1342       empathy_account_widget_handle_params (self,
1343           "entry_id_simple", "account",
1344           "entry_password_simple", "password",
1345           NULL);
1346
1347       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1348
1349       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1350               self->ui_details->gui, "remember_password_simple"));
1351     }
1352   else
1353     {
1354       self->ui_details->gui = empathy_builder_get_file (filename,
1355           "grid_common_msn_settings", &priv->grid_common_settings,
1356           "vbox_msn_settings", &self->ui_details->widget,
1357           NULL);
1358
1359       empathy_account_widget_handle_params (self,
1360           "entry_id", "account",
1361           "entry_password", "password",
1362           "entry_server", "server",
1363           "spinbutton_port", "port",
1364           NULL);
1365
1366       self->ui_details->default_focus = g_strdup ("entry_id");
1367
1368       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1369               self->ui_details->gui, "remember_password"));
1370     }
1371 }
1372
1373 static void
1374 suffix_id_widget_changed_cb (GtkWidget *entry,
1375     EmpathyAccountWidget *self)
1376 {
1377   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1378   const gchar *account;
1379
1380   g_assert (priv->jid_suffix != NULL);
1381
1382   account_widget_entry_changed_common (self, GTK_ENTRY (entry), FALSE);
1383
1384   account = empathy_account_settings_get_string (priv->settings, "account");
1385   if (!EMP_STR_EMPTY (account) &&
1386       !g_str_has_suffix (account, priv->jid_suffix))
1387     {
1388       gchar *tmp;
1389
1390       tmp = g_strdup_printf ("%s%s", account, priv->jid_suffix);
1391
1392       DEBUG ("Change account from '%s' to '%s'", account, tmp);
1393
1394       empathy_account_settings_set_string (priv->settings, "account", tmp);
1395       g_free (tmp);
1396     }
1397
1398   empathy_account_widget_changed (self);
1399 }
1400
1401 static gchar *
1402 remove_jid_suffix (EmpathyAccountWidget *self,
1403     const gchar *str)
1404 {
1405   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1406
1407   g_assert (priv->jid_suffix != NULL);
1408
1409   if (!g_str_has_suffix (str, priv->jid_suffix))
1410     return g_strdup (str);
1411
1412   return g_strndup (str, strlen (str) - strlen (priv->jid_suffix));
1413 }
1414
1415 static void
1416 setup_id_widget_with_suffix (EmpathyAccountWidget *self,
1417     GtkWidget *widget,
1418     const gchar *suffix)
1419 {
1420   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1421   const gchar *str = NULL;
1422
1423   g_object_set_data_full (G_OBJECT (widget), "param_name",
1424       g_strdup ("account"), g_free);
1425
1426   g_assert (priv->jid_suffix == NULL);
1427   priv->jid_suffix = g_strdup (suffix);
1428
1429   str = empathy_account_settings_get_string (priv->settings, "account");
1430   if (str != NULL)
1431     {
1432       gchar *tmp;
1433
1434       tmp = remove_jid_suffix (self, str);
1435       gtk_entry_set_text (GTK_ENTRY (widget), tmp);
1436       g_free (tmp);
1437     }
1438
1439   priv->param_account_widget = widget;
1440
1441   g_signal_connect (widget, "changed",
1442       G_CALLBACK (suffix_id_widget_changed_cb), self);
1443 }
1444
1445 static Service
1446 account_widget_get_service (EmpathyAccountWidget *self)
1447 {
1448   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1449   const gchar *icon_name, *service;
1450
1451   icon_name = empathy_account_settings_get_icon_name (priv->settings);
1452   service = empathy_account_settings_get_service (priv->settings);
1453
1454   /* Previous versions of Empathy didn't set the Service property on Facebook
1455    * and gtalk accounts, so we check using the icon name as well. */
1456   if (!tp_strdiff (icon_name, "im-google-talk") ||
1457       !tp_strdiff (service, "google-talk"))
1458     return GTALK_SERVICE;
1459
1460   if (!tp_strdiff (icon_name, "im-facebook") ||
1461       !tp_strdiff (service, "facebook"))
1462     return FACEBOOK_SERVICE;
1463
1464   return NO_SERVICE;
1465 }
1466
1467 static void
1468 account_widget_build_jabber (EmpathyAccountWidget *self,
1469     const char *filename)
1470 {
1471   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1472   GtkWidget *spinbutton_port;
1473   GtkWidget *checkbutton_ssl;
1474   GtkWidget *label_id, *label_password;
1475   GtkWidget *label_id_create, *label_password_create;
1476   GtkWidget *label_example_fb;
1477   GtkWidget *label_example;
1478   GtkWidget *expander_advanced;
1479   GtkWidget *entry_id;
1480   Service service;
1481
1482   service = account_widget_get_service (self);
1483
1484   empathy_account_settings_set_regex (priv->settings, "account",
1485       ACCOUNT_REGEX_JABBER);
1486
1487   if (priv->simple && service == NO_SERVICE)
1488     {
1489       /* Simple widget for XMPP */
1490       self->ui_details->gui = empathy_builder_get_file (filename,
1491           "vbox_jabber_simple", &self->ui_details->widget,
1492           "label_id_simple", &label_id,
1493           "label_id_create", &label_id_create,
1494           "label_password_simple", &label_password,
1495           "label_password_create", &label_password_create,
1496           NULL);
1497
1498       if (empathy_account_settings_get_boolean (priv->settings, "register"))
1499         {
1500           gtk_widget_hide (label_id);
1501           gtk_widget_hide (label_password);
1502           gtk_widget_show (label_id_create);
1503           gtk_widget_show (label_password_create);
1504         }
1505
1506       empathy_account_widget_handle_params (self,
1507           "entry_id_simple", "account",
1508           "entry_password_simple", "password",
1509           NULL);
1510
1511       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1512
1513       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1514               self->ui_details->gui, "remember_password_simple"));
1515     }
1516   else if (priv->simple && service == GTALK_SERVICE)
1517     {
1518       /* Simple widget for Google Talk */
1519       self->ui_details->gui = empathy_builder_get_file (filename,
1520           "vbox_gtalk_simple", &self->ui_details->widget,
1521           NULL);
1522
1523       empathy_account_widget_handle_params (self,
1524           "entry_id_g_simple", "account",
1525           "entry_password_g_simple", "password",
1526           NULL);
1527
1528       self->ui_details->default_focus = g_strdup ("entry_id_g_simple");
1529
1530       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1531               self->ui_details->gui, "remember_password_g_simple"));
1532     }
1533   else if (priv->simple && service == FACEBOOK_SERVICE)
1534     {
1535       /* Simple widget for Facebook */
1536       self->ui_details->gui = empathy_builder_get_file (filename,
1537           "vbox_fb_simple", &self->ui_details->widget,
1538           "entry_id_fb_simple", &entry_id,
1539           NULL);
1540
1541       empathy_account_widget_handle_params (self,
1542           "entry_password_fb_simple", "password",
1543           NULL);
1544
1545       setup_id_widget_with_suffix (self, entry_id, "@chat.facebook.com");
1546
1547       self->ui_details->default_focus = g_strdup ("entry_id_fb_simple");
1548
1549       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1550               self->ui_details->gui, "remember_password_fb_simple"));
1551     }
1552   else
1553     {
1554       ServiceInfo info = services_infos[service];
1555
1556       /* Full widget for XMPP, Google Talk and Facebook*/
1557       self->ui_details->gui = empathy_builder_get_file (filename,
1558           "grid_common_settings", &priv->grid_common_settings,
1559           "vbox_jabber_settings", &self->ui_details->widget,
1560           "spinbutton_port", &spinbutton_port,
1561           "checkbutton_ssl", &checkbutton_ssl,
1562           "label_username_f_example", &label_example_fb,
1563           info.label_username_example, &label_example,
1564           "expander_advanced", &expander_advanced,
1565           "entry_id", &entry_id,
1566           "label_id", &label_id,
1567           NULL);
1568
1569       empathy_account_widget_handle_params (self,
1570           "entry_password", "password",
1571           "entry_resource", "resource",
1572           "entry_server", "server",
1573           "spinbutton_port", "port",
1574           "spinbutton_priority", "priority",
1575           "checkbutton_ssl", "old-ssl",
1576           "checkbutton_ignore_ssl_errors", "ignore-ssl-errors",
1577           "checkbutton_encryption", "require-encryption",
1578           NULL);
1579
1580       if (service == FACEBOOK_SERVICE)
1581         {
1582           gtk_label_set_label (GTK_LABEL (label_id), _("Username:"));
1583
1584           /* Facebook special case the entry ID widget to hide the
1585            * "@chat.facebook.com" part */
1586           setup_id_widget_with_suffix (self, entry_id, "@chat.facebook.com");
1587         }
1588       else
1589         {
1590           empathy_account_widget_setup_widget (self, entry_id, "account");
1591         }
1592
1593       self->ui_details->default_focus = g_strdup ("entry_id");
1594       priv->spinbutton_port = spinbutton_port;
1595
1596       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1597               self->ui_details->gui, "remember_password"));
1598
1599       g_signal_connect (checkbutton_ssl, "toggled",
1600           G_CALLBACK (account_widget_jabber_ssl_toggled_cb),
1601           self);
1602
1603       if (service == FACEBOOK_SERVICE)
1604         {
1605           GtkContainer *parent;
1606           GList *children;
1607
1608           /* Removing the label from list of focusable widgets */
1609           parent = GTK_CONTAINER (gtk_widget_get_parent (label_example_fb));
1610           children = gtk_container_get_children (parent);
1611           children = g_list_remove (children, label_example_fb);
1612           gtk_container_set_focus_chain (parent, children);
1613           g_list_free (children);
1614         }
1615
1616       gtk_widget_show (label_example);
1617
1618       if (!info.show_advanced)
1619         gtk_widget_hide (expander_advanced);
1620     }
1621 }
1622
1623 static void
1624 account_widget_build_icq (EmpathyAccountWidget *self,
1625     const char *filename)
1626 {
1627   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1628   GtkWidget *spinbutton_port;
1629
1630   empathy_account_settings_set_regex (priv->settings, "account",
1631       ACCOUNT_REGEX_ICQ);
1632
1633   if (priv->simple)
1634     {
1635       self->ui_details->gui = empathy_builder_get_file (filename,
1636           "vbox_icq_simple", &self->ui_details->widget,
1637           NULL);
1638
1639       empathy_account_widget_handle_params (self,
1640           "entry_uin_simple", "account",
1641           "entry_password_simple", "password",
1642           NULL);
1643
1644       self->ui_details->default_focus = g_strdup ("entry_uin_simple");
1645
1646       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1647               self->ui_details->gui, "remember_password_simple"));
1648     }
1649   else
1650     {
1651       self->ui_details->gui = empathy_builder_get_file (filename,
1652           "grid_common_settings", &priv->grid_common_settings,
1653           "vbox_icq_settings", &self->ui_details->widget,
1654           "spinbutton_port", &spinbutton_port,
1655           NULL);
1656
1657       empathy_account_widget_handle_params (self,
1658           "entry_uin", "account",
1659           "entry_password", "password",
1660           "entry_server", "server",
1661           "spinbutton_port", "port",
1662           "entry_charset", "charset",
1663           NULL);
1664
1665       self->ui_details->default_focus = g_strdup ("entry_uin");
1666
1667       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1668               self->ui_details->gui, "remember_password"));
1669     }
1670 }
1671
1672 static void
1673 account_widget_build_aim (EmpathyAccountWidget *self,
1674     const char *filename)
1675 {
1676   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1677   GtkWidget *spinbutton_port;
1678
1679   if (priv->simple)
1680     {
1681       self->ui_details->gui = empathy_builder_get_file (filename,
1682           "vbox_aim_simple", &self->ui_details->widget,
1683           NULL);
1684
1685       empathy_account_widget_handle_params (self,
1686           "entry_screenname_simple", "account",
1687           "entry_password_simple", "password",
1688           NULL);
1689
1690       self->ui_details->default_focus = g_strdup ("entry_screenname_simple");
1691
1692       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1693               self->ui_details->gui, "remember_password_simple"));
1694     }
1695   else
1696     {
1697       self->ui_details->gui = empathy_builder_get_file (filename,
1698           "grid_common_settings", &priv->grid_common_settings,
1699           "vbox_aim_settings", &self->ui_details->widget,
1700           "spinbutton_port", &spinbutton_port,
1701           NULL);
1702
1703       empathy_account_widget_handle_params (self,
1704           "entry_screenname", "account",
1705           "entry_password", "password",
1706           "entry_server", "server",
1707           "spinbutton_port", "port",
1708           NULL);
1709
1710       self->ui_details->default_focus = g_strdup ("entry_screenname");
1711
1712       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1713               self->ui_details->gui, "remember_password"));
1714     }
1715 }
1716
1717 static void
1718 account_widget_build_yahoo (EmpathyAccountWidget *self,
1719     const char *filename)
1720 {
1721   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1722
1723   empathy_account_settings_set_regex (priv->settings, "account",
1724       ACCOUNT_REGEX_YAHOO);
1725
1726   if (priv->simple)
1727     {
1728       self->ui_details->gui = empathy_builder_get_file (filename,
1729           "vbox_yahoo_simple", &self->ui_details->widget,
1730           NULL);
1731
1732       empathy_account_widget_handle_params (self,
1733           "entry_id_simple", "account",
1734           "entry_password_simple", "password",
1735           NULL);
1736
1737       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1738
1739       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1740               self->ui_details->gui, "remember_password_simple"));
1741     }
1742   else
1743     {
1744       self->ui_details->gui = empathy_builder_get_file (filename,
1745           "grid_common_settings", &priv->grid_common_settings,
1746           "vbox_yahoo_settings", &self->ui_details->widget,
1747           NULL);
1748
1749       empathy_account_widget_handle_params (self,
1750           "entry_id", "account",
1751           "entry_password", "password",
1752           "entry_locale", "room-list-locale",
1753           "entry_charset", "charset",
1754           "spinbutton_port", "port",
1755           "checkbutton_ignore_invites", "ignore-invites",
1756           NULL);
1757
1758       self->ui_details->default_focus = g_strdup ("entry_id");
1759
1760       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1761               self->ui_details->gui, "remember_password"));
1762     }
1763 }
1764
1765 static void
1766 account_widget_build_groupwise (EmpathyAccountWidget *self,
1767     const char *filename)
1768 {
1769   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1770
1771   if (priv->simple)
1772     {
1773       self->ui_details->gui = empathy_builder_get_file (filename,
1774           "vbox_groupwise_simple", &self->ui_details->widget,
1775           NULL);
1776
1777       empathy_account_widget_handle_params (self,
1778           "entry_id_simple", "account",
1779           "entry_password_simple", "password",
1780           NULL);
1781
1782       self->ui_details->default_focus = g_strdup ("entry_id_simple");
1783
1784       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1785               self->ui_details->gui, "remember_password_simple"));
1786     }
1787   else
1788     {
1789       self->ui_details->gui = empathy_builder_get_file (filename,
1790           "grid_common_groupwise_settings", &priv->grid_common_settings,
1791           "vbox_groupwise_settings", &self->ui_details->widget,
1792           NULL);
1793
1794       empathy_account_widget_handle_params (self,
1795           "entry_id", "account",
1796           "entry_password", "password",
1797           "entry_server", "server",
1798           "spinbutton_port", "port",
1799           NULL);
1800
1801       self->ui_details->default_focus = g_strdup ("entry_id");
1802
1803       priv->remember_password_widget = GTK_WIDGET (gtk_builder_get_object (
1804               self->ui_details->gui, "remember_password"));
1805     }
1806 }
1807
1808 static void
1809 account_widget_destroy_cb (GtkWidget *widget,
1810     EmpathyAccountWidget *self)
1811 {
1812   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1813   /* set the destroyed flag - workaround */
1814   priv->destroyed = TRUE;
1815
1816   g_object_unref (self);
1817 }
1818
1819 void
1820 empathy_account_widget_set_other_accounts_exist (EmpathyAccountWidget *self,
1821     gboolean others_exist)
1822 {
1823   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1824
1825   priv->other_accounts_exist = others_exist;
1826
1827   if (priv->creating_account)
1828     account_widget_handle_control_buttons_sensitivity (self);
1829 }
1830
1831 static void
1832 do_set_property (GObject *object,
1833     guint prop_id,
1834     const GValue *value,
1835     GParamSpec *pspec)
1836 {
1837   EmpathyAccountWidgetPriv *priv = GET_PRIV (object);
1838
1839   switch (prop_id)
1840     {
1841     case PROP_SETTINGS:
1842       priv->settings = g_value_dup_object (value);
1843       break;
1844     case PROP_SIMPLE:
1845       priv->simple = g_value_get_boolean (value);
1846       break;
1847     case PROP_CREATING_ACCOUNT:
1848       priv->creating_account = g_value_get_boolean (value);
1849       break;
1850     case PROP_OTHER_ACCOUNTS_EXIST:
1851       empathy_account_widget_set_other_accounts_exist (
1852           EMPATHY_ACCOUNT_WIDGET (object), g_value_get_boolean (value));
1853       break;
1854     default:
1855       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1856     }
1857 }
1858
1859 static void
1860 do_get_property (GObject *object,
1861     guint prop_id,
1862     GValue *value,
1863     GParamSpec *pspec)
1864 {
1865   EmpathyAccountWidgetPriv *priv = GET_PRIV (object);
1866
1867   switch (prop_id)
1868     {
1869     case PROP_PROTOCOL:
1870       g_value_set_string (value,
1871         empathy_account_settings_get_protocol (priv->settings));
1872       break;
1873     case PROP_SETTINGS:
1874       g_value_set_object (value, priv->settings);
1875       break;
1876     case PROP_SIMPLE:
1877       g_value_set_boolean (value, priv->simple);
1878       break;
1879     case PROP_CREATING_ACCOUNT:
1880       g_value_set_boolean (value, priv->creating_account);
1881       break;
1882     case PROP_OTHER_ACCOUNTS_EXIST:
1883       g_value_set_boolean (value, priv->other_accounts_exist);
1884       break;
1885     default:
1886       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1887     }
1888 }
1889
1890 static void
1891 set_apply_button (EmpathyAccountWidget *self)
1892 {
1893   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1894   GtkWidget *image;
1895
1896   /* We can't use the stock button as its accelerator ('A') clashes with the
1897    * Add button. */
1898   gtk_button_set_use_stock (GTK_BUTTON (priv->apply_button), FALSE);
1899
1900   gtk_button_set_label (GTK_BUTTON (priv->apply_button), _("A_pply"));
1901   gtk_button_set_use_underline (GTK_BUTTON (priv->apply_button), TRUE);
1902
1903   image = gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON);
1904   gtk_button_set_image (GTK_BUTTON (priv->apply_button), image);
1905 }
1906
1907 static void
1908 presence_changed_cb (TpAccountManager *manager,
1909     TpConnectionPresenceType state,
1910     const gchar *status,
1911     const gchar *message,
1912     EmpathyAccountWidget *self)
1913 {
1914   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1915
1916   if (priv->destroyed)
1917     return;
1918
1919   if (priv->apply_button == NULL)
1920     /* This button doesn't exist in 'simple' mode */
1921     return;
1922
1923   if (state > TP_CONNECTION_PRESENCE_TYPE_OFFLINE &&
1924       priv->creating_account)
1925     {
1926       /* We are online and creating a new account, display a Login button */
1927       GtkWidget *image;
1928
1929       gtk_button_set_use_stock (GTK_BUTTON (priv->apply_button), FALSE);
1930       gtk_button_set_label (GTK_BUTTON (priv->apply_button), _("L_og in"));
1931
1932       image = gtk_image_new_from_stock (GTK_STOCK_CONNECT,
1933           GTK_ICON_SIZE_BUTTON);
1934       gtk_button_set_image (GTK_BUTTON (priv->apply_button), image);
1935     }
1936   else
1937     {
1938       /* We are offline or modifying an existing account, display
1939        * a Save button */
1940       set_apply_button (self);
1941     }
1942 }
1943
1944 static void
1945 account_manager_ready_cb (GObject *source_object,
1946     GAsyncResult *result,
1947     gpointer user_data)
1948 {
1949   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (user_data);
1950   TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
1951   GError *error = NULL;
1952   TpConnectionPresenceType state;
1953
1954   if (!tp_proxy_prepare_finish (account_manager, result, &error))
1955     {
1956       DEBUG ("Failed to prepare account manager: %s", error->message);
1957       g_error_free (error);
1958       goto out;
1959     }
1960
1961   state = tp_account_manager_get_most_available_presence (account_manager, NULL,
1962       NULL);
1963
1964   /* simulate a presence change so the apply button will be changed
1965    * if needed */
1966   presence_changed_cb (account_manager, state, NULL, NULL, self);
1967
1968 out:
1969   g_object_unref (self);
1970 }
1971
1972 #define WIDGET(cm, proto) \
1973   { #cm, #proto, "empathy-account-widget-"#proto".ui", \
1974     account_widget_build_##proto }
1975
1976 #ifndef HAVE_MEEGO
1977 /* Meego doesn't support registration */
1978 static void
1979 add_register_buttons (EmpathyAccountWidget *self,
1980     TpAccount *account)
1981 {
1982   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1983   const TpConnectionManagerProtocol *protocol;
1984   GtkWidget *radiobutton_register;
1985   GtkWidget *vbox = self->ui_details->widget;
1986
1987   if (!priv->creating_account)
1988     return;
1989
1990   protocol = empathy_account_settings_get_tp_protocol (priv->settings);
1991   if (protocol == NULL)
1992     return;
1993
1994   if (!tp_connection_manager_protocol_can_register (protocol))
1995     return;
1996
1997   if (account_widget_get_service (self) != NO_SERVICE)
1998     return;
1999
2000   if (priv->simple)
2001     return;
2002
2003   priv->radiobutton_reuse = gtk_radio_button_new_with_label (NULL,
2004       _("This account already exists on the server"));
2005   radiobutton_register = gtk_radio_button_new_with_label (
2006       gtk_radio_button_get_group (GTK_RADIO_BUTTON (priv->radiobutton_reuse)),
2007       _("Create a new account on the server"));
2008
2009   gtk_box_pack_start (GTK_BOX (vbox), priv->radiobutton_reuse, FALSE, FALSE, 0);
2010   gtk_box_pack_start (GTK_BOX (vbox), radiobutton_register, FALSE, FALSE, 0);
2011   gtk_box_reorder_child (GTK_BOX (vbox), priv->radiobutton_reuse, 0);
2012   gtk_box_reorder_child (GTK_BOX (vbox), radiobutton_register, 1);
2013   gtk_widget_show (priv->radiobutton_reuse);
2014   gtk_widget_show (radiobutton_register);
2015 }
2016 #endif /* HAVE_MEEGO */
2017
2018 static void
2019 remember_password_toggled_cb (GtkToggleButton *button,
2020     EmpathyAccountWidget *self)
2021 {
2022   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2023
2024   if (gtk_toggle_button_get_active (button))
2025     {
2026       gtk_widget_set_sensitive (priv->param_password_widget, TRUE);
2027     }
2028   else
2029     {
2030       gtk_widget_set_sensitive (priv->param_password_widget, FALSE);
2031       gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), "");
2032       empathy_account_settings_unset (priv->settings, "password");
2033     }
2034 }
2035
2036 static void
2037 account_settings_password_retrieved_cb (GObject *object,
2038     gpointer user_data)
2039 {
2040   EmpathyAccountWidget *self = user_data;
2041   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2042   const gchar *password = empathy_account_settings_get_string (
2043       priv->settings, "password");
2044
2045   if (password != NULL)
2046     {
2047       /* We have to do this so that when we call gtk_entry_set_text,
2048        * the ::changed callback doesn't think the user made the
2049        * change. */
2050       priv->automatic_change = TRUE;
2051       gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), password);
2052       priv->automatic_change = FALSE;
2053     }
2054
2055   gtk_toggle_button_set_active (
2056       GTK_TOGGLE_BUTTON (priv->remember_password_widget),
2057       !EMP_STR_EMPTY (password));
2058 }
2059
2060 static void
2061 do_constructed (GObject *obj)
2062 {
2063   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2064   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2065   TpAccount *account;
2066   TpStorageRestrictionFlags storage_restrictions;
2067   const gchar *display_name, *default_display_name;
2068   guint i = 0;
2069   struct {
2070     const gchar *cm_name;
2071     const gchar *protocol;
2072     const char *file;
2073     void (*func)(EmpathyAccountWidget *self, const gchar *filename);
2074   } widgets [] = {
2075     { "salut", "local-xmpp", "empathy-account-widget-local-xmpp.ui",
2076         account_widget_build_salut },
2077     WIDGET (gabble, jabber),
2078     WIDGET (butterfly, msn),
2079     WIDGET (haze, icq),
2080     WIDGET (haze, aim),
2081     WIDGET (haze, yahoo),
2082     WIDGET (haze, groupwise),
2083     WIDGET (idle, irc),
2084     WIDGET (sofiasip, sip),
2085   };
2086
2087   account = empathy_account_settings_get_account (priv->settings);
2088
2089   if (account != NULL)
2090     storage_restrictions = tp_account_get_storage_restrictions (account);
2091   else
2092     storage_restrictions = 0;
2093
2094   /* Empathy can only edit accounts without the Cannot_Set_Parameters flag */
2095   if (storage_restrictions & TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS)
2096     {
2097       DEBUG ("Account is provided by an external storage provider");
2098
2099       account_widget_build_external (self, priv->settings);
2100     }
2101   else
2102     {
2103       const gchar *protocol, *cm_name;
2104
2105       cm_name = empathy_account_settings_get_cm (priv->settings);
2106       protocol = empathy_account_settings_get_protocol (priv->settings);
2107
2108       for (i = 0 ; i < G_N_ELEMENTS (widgets); i++)
2109         {
2110           if (!tp_strdiff (widgets[i].cm_name, cm_name) &&
2111               !tp_strdiff (widgets[i].protocol, protocol))
2112             {
2113               gchar *filename;
2114
2115               filename = empathy_file_lookup (widgets[i].file,
2116                   "libempathy-gtk");
2117               widgets[i].func (self, filename);
2118               g_free (filename);
2119
2120               break;
2121             }
2122         }
2123
2124       if (i == G_N_ELEMENTS (widgets))
2125         {
2126           gchar *filename = empathy_file_lookup (
2127               "empathy-account-widget-generic.ui", "libempathy-gtk");
2128           account_widget_build_generic (self, filename);
2129           g_free (filename);
2130         }
2131     }
2132
2133   /* handle default focus */
2134   if (self->ui_details->default_focus != NULL)
2135     {
2136       GObject *default_focus_entry;
2137
2138       default_focus_entry = gtk_builder_get_object
2139         (self->ui_details->gui, self->ui_details->default_focus);
2140       g_signal_connect (default_focus_entry, "realize",
2141           G_CALLBACK (gtk_widget_grab_focus),
2142           NULL);
2143     }
2144
2145   /* remember password */
2146   if (priv->param_password_widget != NULL
2147       && priv->remember_password_widget != NULL
2148       && empathy_account_settings_supports_sasl (priv->settings))
2149     {
2150       if (priv->simple)
2151         {
2152           gtk_toggle_button_set_active (
2153               GTK_TOGGLE_BUTTON (priv->remember_password_widget), TRUE);
2154         }
2155       else
2156         {
2157           gtk_toggle_button_set_active (
2158               GTK_TOGGLE_BUTTON (priv->remember_password_widget),
2159               !EMP_STR_EMPTY (empathy_account_settings_get_string (
2160                       priv->settings, "password")));
2161
2162           /* The password might not have been retrieved from the
2163            * keyring yet. We should update the remember password
2164            * toggle button and the password entry when/if it is. */
2165           tp_g_signal_connect_object (priv->settings, "password-retrieved",
2166               G_CALLBACK (account_settings_password_retrieved_cb), self, 0);
2167         }
2168
2169       g_signal_connect (priv->remember_password_widget, "toggled",
2170           G_CALLBACK (remember_password_toggled_cb), self);
2171
2172       remember_password_toggled_cb (
2173           GTK_TOGGLE_BUTTON (priv->remember_password_widget), self);
2174     }
2175   else if (priv->remember_password_widget != NULL
2176       && !empathy_account_settings_supports_sasl (priv->settings))
2177     {
2178       gtk_widget_set_visible (priv->remember_password_widget, FALSE);
2179     }
2180
2181   /* dup and init the account-manager */
2182   priv->account_manager = tp_account_manager_dup ();
2183
2184   g_object_ref (self);
2185   tp_proxy_prepare_async (priv->account_manager, NULL,
2186       account_manager_ready_cb, self);
2187
2188   /* handle apply and cancel button */
2189   if (!priv->simple &&
2190       !(storage_restrictions &
2191         TP_STORAGE_RESTRICTION_FLAG_CANNOT_SET_PARAMETERS))
2192     {
2193       GtkWidget *hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
2194       GtkWidget *image;
2195
2196       gtk_box_set_homogeneous (hbox, TRUE);
2197
2198       /*  We can't use the stock button as its accelerator ('C') clashes with
2199        *  the Close button. */
2200       priv->cancel_button = gtk_button_new ();
2201       gtk_button_set_label (GTK_BUTTON (priv->cancel_button), _("Ca_ncel"));
2202       gtk_button_set_use_underline (GTK_BUTTON (priv->cancel_button), TRUE);
2203
2204       image = gtk_image_new_from_stock (GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
2205       gtk_button_set_image (GTK_BUTTON (priv->cancel_button), image);
2206
2207       priv->apply_button = gtk_button_new ();
2208       set_apply_button (self);
2209
2210       /* We'll change this button to a "Log in" one if we are creating a new
2211        * account and are connected. */
2212       tp_g_signal_connect_object (priv->account_manager,
2213           "most-available-presence-changed",
2214           G_CALLBACK (presence_changed_cb), obj, 0);
2215
2216       gtk_box_pack_end (GTK_BOX (hbox), priv->apply_button, TRUE,
2217           TRUE, 3);
2218       gtk_box_pack_end (GTK_BOX (hbox), priv->cancel_button, TRUE,
2219           TRUE, 3);
2220
2221       gtk_box_pack_end (GTK_BOX (self->ui_details->widget), hbox, FALSE,
2222           FALSE, 3);
2223
2224       g_signal_connect (priv->cancel_button, "clicked",
2225           G_CALLBACK (account_widget_cancel_clicked_cb),
2226           self);
2227       g_signal_connect (priv->apply_button, "clicked",
2228           G_CALLBACK (account_widget_apply_clicked_cb),
2229           self);
2230       gtk_widget_show_all (hbox);
2231
2232       if (priv->creating_account)
2233         /* When creating an account, the user might have nothing to enter.
2234          * That means that no control interaction might occur,
2235          * so we update the control button sensitivity manually.
2236          */
2237         account_widget_handle_control_buttons_sensitivity (self);
2238       else
2239         account_widget_set_control_buttons_sensitivity (self, FALSE);
2240     }
2241
2242 #ifndef HAVE_MEEGO
2243   add_register_buttons (self, account);
2244 #endif /* HAVE_MEEGO */
2245
2246   /* hook up to widget destruction to unref ourselves */
2247   g_signal_connect (self->ui_details->widget, "destroy",
2248       G_CALLBACK (account_widget_destroy_cb), self);
2249
2250   if (self->ui_details->gui != NULL)
2251     {
2252       empathy_builder_unref_and_keep_widget (self->ui_details->gui,
2253           self->ui_details->widget);
2254       self->ui_details->gui = NULL;
2255     }
2256
2257   display_name = empathy_account_settings_get_display_name (priv->settings);
2258   default_display_name = empathy_account_widget_get_default_display_name (self);
2259
2260   if (tp_strdiff (display_name, default_display_name) &&
2261       !priv->creating_account)
2262     {
2263       /* The display name of the account is not the one that we'd assign by
2264        * default; assume that the user changed it manually */
2265       g_object_set (priv->settings, "display-name-overridden", TRUE, NULL);
2266     }
2267 }
2268
2269 static void
2270 do_dispose (GObject *obj)
2271 {
2272   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2273   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2274
2275   if (priv->dispose_run)
2276     return;
2277
2278   priv->dispose_run = TRUE;
2279
2280   if (priv->settings != NULL)
2281     {
2282       g_object_unref (priv->settings);
2283       priv->settings = NULL;
2284     }
2285
2286   if (priv->account_manager != NULL)
2287     {
2288       g_object_unref (priv->account_manager);
2289       priv->account_manager = NULL;
2290     }
2291
2292   if (G_OBJECT_CLASS (empathy_account_widget_parent_class)->dispose != NULL)
2293     G_OBJECT_CLASS (empathy_account_widget_parent_class)->dispose (obj);
2294 }
2295
2296 static void
2297 do_finalize (GObject *obj)
2298 {
2299   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
2300   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2301
2302   g_free (self->ui_details->default_focus);
2303   g_slice_free (EmpathyAccountWidgetUIDetails, self->ui_details);
2304
2305   g_free (priv->jid_suffix);
2306
2307   if (G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize != NULL)
2308     G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize (obj);
2309 }
2310
2311 static void
2312 empathy_account_widget_class_init (EmpathyAccountWidgetClass *klass)
2313 {
2314   GObjectClass *oclass = G_OBJECT_CLASS (klass);
2315   GParamSpec *param_spec;
2316
2317   oclass->get_property = do_get_property;
2318   oclass->set_property = do_set_property;
2319   oclass->constructed = do_constructed;
2320   oclass->dispose = do_dispose;
2321   oclass->finalize = do_finalize;
2322
2323   param_spec = g_param_spec_string ("protocol",
2324       "protocol", "The protocol of the account",
2325       NULL,
2326       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2327   g_object_class_install_property (oclass, PROP_PROTOCOL, param_spec);
2328
2329   param_spec = g_param_spec_object ("settings",
2330       "settings", "The settings of the account",
2331       EMPATHY_TYPE_ACCOUNT_SETTINGS,
2332       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2333   g_object_class_install_property (oclass, PROP_SETTINGS, param_spec);
2334
2335   param_spec = g_param_spec_boolean ("simple",
2336       "simple", "Whether the account widget is a simple or an advanced one",
2337       FALSE,
2338       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2339   g_object_class_install_property (oclass, PROP_SIMPLE, param_spec);
2340
2341   param_spec = g_param_spec_boolean ("creating-account",
2342       "creating-account",
2343       "TRUE if we're creating an account, FALSE if we're modifying it",
2344       FALSE,
2345       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2346   g_object_class_install_property (oclass, PROP_CREATING_ACCOUNT, param_spec);
2347
2348   param_spec = g_param_spec_boolean ("other-accounts-exist",
2349       "other-accounts-exist",
2350       "TRUE if there are any other accounts (even if this isn't yet saved)",
2351       FALSE,
2352       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
2353   g_object_class_install_property (oclass, PROP_OTHER_ACCOUNTS_EXIST,
2354                   param_spec);
2355
2356   signals[HANDLE_APPLY] =
2357     g_signal_new ("handle-apply", G_TYPE_FROM_CLASS (klass),
2358         G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2359         g_cclosure_marshal_generic,
2360         G_TYPE_NONE,
2361         1, G_TYPE_BOOLEAN);
2362
2363   /* This signal is emitted when an account has been created and enabled. */
2364   signals[ACCOUNT_CREATED] =
2365       g_signal_new ("account-created", 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_OBJECT);
2370
2371   signals[CANCELLED] =
2372       g_signal_new ("cancelled", G_TYPE_FROM_CLASS (klass),
2373           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
2374           g_cclosure_marshal_generic,
2375           G_TYPE_NONE,
2376           0);
2377
2378   g_type_class_add_private (klass, sizeof (EmpathyAccountWidgetPriv));
2379 }
2380
2381 static void
2382 empathy_account_widget_init (EmpathyAccountWidget *self)
2383 {
2384   EmpathyAccountWidgetPriv *priv =
2385     G_TYPE_INSTANCE_GET_PRIVATE ((self), EMPATHY_TYPE_ACCOUNT_WIDGET,
2386         EmpathyAccountWidgetPriv);
2387
2388   self->priv = priv;
2389   priv->dispose_run = FALSE;
2390
2391   self->ui_details = g_slice_new0 (EmpathyAccountWidgetUIDetails);
2392 }
2393
2394 /* public methods */
2395
2396 void
2397 empathy_account_widget_discard_pending_changes
2398     (EmpathyAccountWidget *widget)
2399 {
2400   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
2401
2402   empathy_account_settings_discard_changes (priv->settings);
2403   priv->contains_pending_changes = FALSE;
2404 }
2405
2406 gboolean
2407 empathy_account_widget_contains_pending_changes (EmpathyAccountWidget *widget)
2408 {
2409   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
2410
2411   return priv->contains_pending_changes;
2412 }
2413
2414 void
2415 empathy_account_widget_handle_params (EmpathyAccountWidget *self,
2416     const gchar *first_widget,
2417     ...)
2418 {
2419   va_list args;
2420
2421   va_start (args, first_widget);
2422   account_widget_handle_params_valist (self, first_widget, args);
2423   va_end (args);
2424 }
2425
2426 GtkWidget *
2427 empathy_account_widget_get_widget (EmpathyAccountWidget *widget)
2428 {
2429   return widget->ui_details->widget;
2430 }
2431
2432 EmpathyAccountWidget *
2433 empathy_account_widget_new_for_protocol (EmpathyAccountSettings *settings,
2434     gboolean simple)
2435 {
2436   EmpathyAccountWidget *self;
2437
2438   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), NULL);
2439
2440   self = g_object_new
2441     (EMPATHY_TYPE_ACCOUNT_WIDGET,
2442         "settings", settings, "simple", simple,
2443         "creating-account",
2444         empathy_account_settings_get_account (settings) == NULL,
2445         NULL);
2446
2447   return self;
2448 }
2449
2450 gchar *
2451 empathy_account_widget_get_default_display_name (EmpathyAccountWidget *self)
2452 {
2453   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2454   const gchar *login_id;
2455   const gchar *protocol, *p;
2456   gchar *default_display_name;
2457   Service service;
2458
2459   login_id = empathy_account_settings_get_string (priv->settings, "account");
2460   protocol = empathy_account_settings_get_protocol (priv->settings);
2461   service = account_widget_get_service (self);
2462
2463   if (login_id != NULL)
2464     {
2465       /* TODO: this should be done in empathy-account-widget-irc */
2466       if (!tp_strdiff (protocol, "irc"))
2467         {
2468           EmpathyIrcNetwork *network;
2469
2470           network = empathy_irc_network_chooser_get_network (
2471               priv->irc_network_chooser);
2472           g_assert (network != NULL);
2473
2474           /* To translators: The first parameter is the login id and the
2475            * second one is the network. The resulting string will be something
2476            * like: "MyUserName on freenode".
2477            * You should reverse the order of these arguments if the
2478            * server should come before the login id in your locale.*/
2479           default_display_name = g_strdup_printf (_("%1$s on %2$s"),
2480               login_id, empathy_irc_network_get_name (network));
2481         }
2482       else if (service == FACEBOOK_SERVICE && priv->jid_suffix != NULL)
2483         {
2484           gchar *tmp;
2485
2486           tmp = remove_jid_suffix (self, login_id);
2487           default_display_name = g_strdup_printf ("Facebook (%s)", tmp);
2488           g_free (tmp);
2489         }
2490       else
2491         {
2492           default_display_name = g_strdup (login_id);
2493         }
2494
2495       return default_display_name;
2496     }
2497
2498   if ((p = empathy_protocol_name_to_display_name (protocol)) != NULL)
2499     protocol = p;
2500
2501   if (protocol != NULL)
2502     {
2503       /* To translators: The parameter is the protocol name. The resulting
2504        * string will be something like: "Jabber Account" */
2505       default_display_name = g_strdup_printf (_("%s Account"), protocol);
2506     }
2507   else
2508     {
2509       default_display_name = g_strdup (_("New account"));
2510     }
2511
2512   return default_display_name;
2513 }
2514
2515 /* Used by subclass to indicate that widget contains pending changes */
2516 void
2517 empathy_account_widget_changed (EmpathyAccountWidget *self)
2518 {
2519   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2520
2521   account_widget_handle_control_buttons_sensitivity (self);
2522   priv->contains_pending_changes = TRUE;
2523 }
2524
2525 void
2526 empathy_account_widget_set_account_param (EmpathyAccountWidget *self,
2527     const gchar *account)
2528 {
2529   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2530
2531   if (priv->param_account_widget == NULL)
2532     return;
2533
2534   gtk_entry_set_text (GTK_ENTRY (priv->param_account_widget), account);
2535 }
2536
2537 void
2538 empathy_account_widget_set_password_param (EmpathyAccountWidget *self,
2539     const gchar *account)
2540 {
2541   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2542
2543   if (priv->param_password_widget == NULL)
2544     return;
2545
2546   gtk_entry_set_text (GTK_ENTRY (priv->param_password_widget), account);
2547 }
2548
2549 EmpathyAccountSettings *
2550 empathy_account_widget_get_settings (EmpathyAccountWidget *self)
2551 {
2552   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
2553
2554   return priv->settings;
2555 }