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