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