]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-account-widget.c
Fix errors revealed by make check
[empathy.git] / libempathy-gtk / empathy-account-widget.c
1 /*
2  * Copyright (C) 2006-2007 Imendio AB
3  * Copyright (C) 2007-2009 Collabora Ltd.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA  02110-1301  USA
19  *
20  * Authors: Xavier Claessens <xclaesse@gmail.com>
21  *          Martyn Russell <martyn@imendio.com>
22  *          Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
23  *          Jonathan Tellier <jonathan.tellier@gmail.com>
24  */
25
26 #include <config.h>
27
28 #include <string.h>
29
30 #include <gtk/gtk.h>
31 #include <glib/gi18n-lib.h>
32
33 #include <libempathy/empathy-utils.h>
34 #include <libempathy/empathy-account.h>
35
36 #include <telepathy-glib/connection-manager.h>
37 #include <telepathy-glib/util.h>
38 #include <dbus/dbus-protocol.h>
39
40 #include "empathy-account-widget.h"
41 #include "empathy-account-widget-private.h"
42 #include "empathy-account-widget-sip.h"
43 #include "empathy-account-widget-irc.h"
44 #include "empathy-ui-utils.h"
45
46 #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
47 #include <libempathy/empathy-debug.h>
48
49 G_DEFINE_TYPE (EmpathyAccountWidget, empathy_account_widget, G_TYPE_OBJECT)
50
51 typedef struct {
52   char *protocol;
53   EmpathyAccountSettings *settings;
54
55   GtkWidget *table_common_settings;
56   GtkWidget *apply_button;
57   GtkWidget *cancel_button;
58   GtkWidget *entry_password;
59   GtkWidget *button_forget;
60   GtkWidget *spinbutton_port;
61   GtkWidget *enabled_checkbox;
62
63   gboolean simple;
64
65   /* An EmpathyAccountWidget can be used to either create an account or
66    * modify it. When we are creating an account, this member is set to TRUE */
67   gboolean creating_account;
68
69   /* After having applied changes to a user account, we automatically
70    * disconnect him. Once he's disconnected, he will be reconnected,
71    * depending on the value of this member which should be set to the checked
72    * state of the "Enabled" checkbox. This is done so the new information
73    * entered by the user is validated on the server. */
74   gboolean re_enable_accound;
75
76   gboolean dispose_run;
77 } EmpathyAccountWidgetPriv;
78
79 enum {
80   PROP_PROTOCOL = 1,
81   PROP_SETTINGS,
82   PROP_SIMPLE,
83   PROP_CREATING_ACCOUNT
84 };
85
86 enum {
87   HANDLE_APPLY,
88   ACCOUNT_CREATED,
89   CANCELLED,
90   LAST_SIGNAL
91 };
92
93 static guint signals[LAST_SIGNAL] = { 0 };
94
95 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAccountWidget)
96 #define CHANGED_TIMEOUT 300
97
98 static void
99 account_widget_set_control_buttons_sensitivity (EmpathyAccountWidget *self,
100     gboolean sensitive)
101 {
102   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
103
104   if (!priv->simple)
105     {
106       gtk_widget_set_sensitive (priv->apply_button, sensitive);
107       gtk_widget_set_sensitive (priv->cancel_button, sensitive);
108     }
109 }
110
111 static void
112 account_widget_handle_control_buttons_sensitivity (EmpathyAccountWidget *self)
113 {
114   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
115   gboolean is_valid;
116
117   is_valid = empathy_account_settings_is_valid (priv->settings);
118
119   if (!priv->simple)
120     {
121       gtk_widget_set_sensitive (priv->apply_button, is_valid);
122       gtk_widget_set_sensitive (priv->cancel_button, is_valid);
123     }
124
125   g_signal_emit (self, signals[HANDLE_APPLY], 0, is_valid);
126 }
127
128 static void
129 account_widget_entry_changed_common (EmpathyAccountWidget *self,
130     GtkEntry *entry, gboolean focus)
131 {
132   const gchar *str;
133   const gchar *param_name;
134   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
135
136   str = gtk_entry_get_text (entry);
137   param_name = g_object_get_data (G_OBJECT (entry), "param_name");
138
139   if (EMP_STR_EMPTY (str))
140     {
141       const gchar *value = NULL;
142
143       empathy_account_settings_unset (priv->settings, param_name);
144
145       if (focus)
146         {
147           value = empathy_account_settings_get_string (priv->settings,
148               param_name);
149           DEBUG ("Unset %s and restore to %s", param_name, value);
150           gtk_entry_set_text (entry, value ? value : "");
151         }
152     }
153   else
154     {
155       DEBUG ("Setting %s to %s", param_name,
156           tp_strdiff (param_name, "password") ? str : "***");
157       empathy_account_settings_set_string (priv->settings, param_name, str);
158     }
159 }
160
161 static gboolean
162 account_widget_entry_focus_cb (GtkWidget *widget,
163     GdkEventFocus *event,
164     EmpathyAccountWidget *self)
165 {
166   account_widget_entry_changed_common (self, GTK_ENTRY (widget), TRUE);
167
168   return FALSE;
169 }
170
171 static void
172 account_widget_entry_changed_cb (GtkEditable *entry,
173     EmpathyAccountWidget *self)
174 {
175   account_widget_entry_changed_common (self, GTK_ENTRY (entry), FALSE);
176   account_widget_handle_control_buttons_sensitivity (self);
177 }
178
179 static void
180 account_widget_int_changed_cb (GtkWidget *widget,
181     EmpathyAccountWidget *self)
182 {
183   const gchar *param_name;
184   gint value;
185   const gchar *signature;
186   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
187
188   value = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
189   param_name = g_object_get_data (G_OBJECT (widget), "param_name");
190
191   signature = empathy_account_settings_get_dbus_signature (priv->settings,
192     param_name);
193   g_return_if_fail (signature != NULL);
194
195   DEBUG ("Setting %s to %d", param_name, value);
196
197   switch ((int)*signature)
198     {
199     case DBUS_TYPE_INT16:
200     case DBUS_TYPE_INT32:
201       empathy_account_settings_set_int32 (priv->settings, param_name, value);
202       break;
203     case DBUS_TYPE_INT64:
204       empathy_account_settings_set_int64 (priv->settings, param_name, value);
205       break;
206     case DBUS_TYPE_UINT16:
207     case DBUS_TYPE_UINT32:
208       empathy_account_settings_set_uint32 (priv->settings, param_name, value);
209       break;
210     case DBUS_TYPE_UINT64:
211       empathy_account_settings_set_uint64 (priv->settings, param_name, value);
212       break;
213     default:
214       g_return_if_reached ();
215     }
216
217   account_widget_handle_control_buttons_sensitivity (self);
218 }
219
220 static void
221 account_widget_checkbutton_toggled_cb (GtkWidget *widget,
222     EmpathyAccountWidget *self)
223 {
224   gboolean     value;
225   gboolean     default_value;
226   const gchar *param_name;
227   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
228
229   value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
230   param_name = g_object_get_data (G_OBJECT (widget), "param_name");
231
232   /* FIXME: This is ugly! checkbox don't have a "not-set" value so we
233    * always unset the param and set the value if different from the
234    * default value. */
235   empathy_account_settings_unset (priv->settings, param_name);
236   default_value = empathy_account_settings_get_boolean (priv->settings,
237       param_name);
238
239   if (default_value == value)
240     {
241       DEBUG ("Unset %s and restore to %d", param_name, default_value);
242     }
243   else
244     {
245       DEBUG ("Setting %s to %d", param_name, value);
246       empathy_account_settings_set_boolean (priv->settings, param_name, value);
247     }
248
249   account_widget_handle_control_buttons_sensitivity (self);
250 }
251
252 static void
253 account_widget_forget_clicked_cb (GtkWidget *button,
254     EmpathyAccountWidget *self)
255 {
256   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
257   const gchar *param_name;
258
259   param_name = g_object_get_data (G_OBJECT (priv->entry_password),
260       "param_name");
261
262   DEBUG ("Unset %s", param_name);
263   empathy_account_settings_unset (priv->settings, param_name);
264   gtk_entry_set_text (GTK_ENTRY (priv->entry_password), "");
265
266   account_widget_handle_control_buttons_sensitivity (self);
267 }
268
269 static void
270 account_widget_password_changed_cb (GtkWidget *entry,
271     EmpathyAccountWidget *self)
272 {
273   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
274   const gchar *str;
275
276   str = gtk_entry_get_text (GTK_ENTRY (entry));
277   gtk_widget_set_sensitive (priv->button_forget, !EMP_STR_EMPTY (str));
278 }
279
280 static void
281 account_widget_jabber_ssl_toggled_cb (GtkWidget *checkbutton_ssl,
282     EmpathyAccountWidget *self)
283 {
284   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
285   gboolean   value;
286   gint32       port = 0;
287
288   value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbutton_ssl));
289   port = empathy_account_settings_get_uint32 (priv->settings, "port");
290
291   if (value)
292     {
293       if (port == 5222 || port == 0)
294         port = 5223;
295     }
296   else
297     {
298       if (port == 5223 || port == 0)
299         port = 5222;
300     }
301
302   gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->spinbutton_port), port);
303 }
304
305 static void
306 account_widget_setup_widget (EmpathyAccountWidget *self,
307     GtkWidget *widget,
308     const gchar *param_name)
309 {
310   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
311
312   g_object_set_data_full (G_OBJECT (widget), "param_name",
313       g_strdup (param_name), g_free);
314
315   if (GTK_IS_SPIN_BUTTON (widget))
316     {
317       gint value = 0;
318       const gchar *signature;
319
320       signature = empathy_account_settings_get_dbus_signature (priv->settings,
321           param_name);
322       g_return_if_fail (signature != NULL);
323
324       switch ((int)*signature)
325         {
326           case DBUS_TYPE_INT16:
327           case DBUS_TYPE_INT32:
328             value = empathy_account_settings_get_int32 (priv->settings,
329               param_name);
330             break;
331           case DBUS_TYPE_INT64:
332             value = empathy_account_settings_get_int64 (priv->settings,
333               param_name);
334             break;
335           case DBUS_TYPE_UINT16:
336           case DBUS_TYPE_UINT32:
337             value = empathy_account_settings_get_uint32 (priv->settings,
338               param_name);
339             break;
340           case DBUS_TYPE_UINT64:
341             value = empathy_account_settings_get_uint64 (priv->settings,
342                 param_name);
343             break;
344           default:
345             g_return_if_reached ();
346         }
347
348       gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value);
349
350       g_signal_connect (widget, "value-changed",
351           G_CALLBACK (account_widget_int_changed_cb),
352           self);
353     }
354   else if (GTK_IS_ENTRY (widget))
355     {
356       const gchar *str = NULL;
357
358       str = empathy_account_settings_get_string (priv->settings, param_name);
359       gtk_entry_set_text (GTK_ENTRY (widget), str ? str : "");
360
361       if (strstr (param_name, "password"))
362         {
363           gtk_entry_set_visibility (GTK_ENTRY (widget), FALSE);
364         }
365
366       g_signal_connect (widget, "focus-out-event",
367           G_CALLBACK (account_widget_entry_focus_cb),
368           self);
369       g_signal_connect (widget, "changed",
370           G_CALLBACK (account_widget_entry_changed_cb), self);
371     }
372   else if (GTK_IS_TOGGLE_BUTTON (widget))
373     {
374       gboolean value = FALSE;
375
376       value = empathy_account_settings_get_boolean (priv->settings,
377           param_name);
378       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value);
379
380       g_signal_connect (widget, "toggled",
381           G_CALLBACK (account_widget_checkbutton_toggled_cb),
382           self);
383     }
384   else
385     {
386       DEBUG ("Unknown type of widget for param %s", param_name);
387     }
388 }
389
390 static gchar *
391 account_widget_generic_format_param_name (const gchar *param_name)
392 {
393   gchar *str;
394   gchar *p;
395
396   str = g_strdup (param_name);
397
398   if (str && g_ascii_isalpha (str[0]))
399     str[0] = g_ascii_toupper (str[0]);
400
401   while ((p = strchr (str, '-')) != NULL)
402     {
403       if (p[1] != '\0' && g_ascii_isalpha (p[1]))
404         {
405           p[0] = ' ';
406           p[1] = g_ascii_toupper (p[1]);
407         }
408
409       p++;
410     }
411
412   return str;
413 }
414
415 static void
416 accounts_widget_generic_setup (EmpathyAccountWidget *self,
417     GtkWidget *table_common_settings,
418     GtkWidget *table_advanced_settings)
419 {
420   TpConnectionManagerParam *params, *param;
421   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
422
423   params = empathy_account_settings_get_tp_params (priv->settings);
424
425   for (param = params; param != NULL && param->name != NULL; param++)
426     {
427       GtkWidget       *table_settings;
428       guint            n_rows = 0;
429       GtkWidget       *widget = NULL;
430       gchar           *param_name_formatted;
431
432       if (param->flags & TP_CONN_MGR_PARAM_FLAG_REQUIRED)
433         table_settings = table_common_settings;
434       else if (priv->simple)
435         return;
436       else
437         table_settings = table_advanced_settings;
438
439       param_name_formatted = account_widget_generic_format_param_name
440         (param->name);
441       g_object_get (table_settings, "n-rows", &n_rows, NULL);
442       gtk_table_resize (GTK_TABLE (table_settings), ++n_rows, 2);
443
444       if (param->dbus_signature[0] == 's')
445         {
446           gchar *str;
447
448           str = g_strdup_printf (_("%s:"), param_name_formatted);
449           widget = gtk_label_new (str);
450           gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
451           g_free (str);
452
453           gtk_table_attach (GTK_TABLE (table_settings),
454               widget,
455               0, 1,
456               n_rows - 1, n_rows,
457               GTK_FILL, 0,
458               0, 0);
459           gtk_widget_show (widget);
460
461           widget = gtk_entry_new ();
462           if (strcmp (param->name, "account") == 0)
463             {
464               g_signal_connect (widget, "realize",
465                   G_CALLBACK (gtk_widget_grab_focus),
466                   NULL);
467             }
468           gtk_table_attach (GTK_TABLE (table_settings),
469               widget,
470               1, 2,
471               n_rows - 1, n_rows,
472               GTK_FILL | GTK_EXPAND, 0,
473               0, 0);
474           gtk_widget_show (widget);
475         }
476       /* int types: ynqiuxt. double type is 'd' */
477       else if (param->dbus_signature[0] == 'y' ||
478           param->dbus_signature[0] == 'n' ||
479           param->dbus_signature[0] == 'q' ||
480           param->dbus_signature[0] == 'i' ||
481           param->dbus_signature[0] == 'u' ||
482           param->dbus_signature[0] == 'x' ||
483           param->dbus_signature[0] == 't' ||
484           param->dbus_signature[0] == 'd')
485         {
486           gchar   *str = NULL;
487           gdouble  minint = 0;
488           gdouble  maxint = 0;
489           gdouble  step = 1;
490
491           switch (param->dbus_signature[0])
492             {
493             case 'y': minint = G_MININT8;  maxint = G_MAXINT8;   break;
494             case 'n': minint = G_MININT16; maxint = G_MAXINT16;  break;
495             case 'q': minint = 0;          maxint = G_MAXUINT16; break;
496             case 'i': minint = G_MININT32; maxint = G_MAXINT32;  break;
497             case 'u': minint = 0;          maxint = G_MAXUINT32; break;
498             case 'x': minint = G_MININT64; maxint = G_MAXINT64;  break;
499             case 't': minint = 0;          maxint = G_MAXUINT64; break;
500             case 'd': minint = G_MININT32; maxint = G_MAXINT32;
501               step = 0.1; break;
502             }
503
504           str = g_strdup_printf (_("%s:"), param_name_formatted);
505           widget = gtk_label_new (str);
506           gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
507           g_free (str);
508
509           gtk_table_attach (GTK_TABLE (table_settings),
510               widget,
511               0, 1,
512               n_rows - 1, n_rows,
513               GTK_FILL, 0,
514               0, 0);
515           gtk_widget_show (widget);
516
517           widget = gtk_spin_button_new_with_range (minint, maxint, step);
518           gtk_table_attach (GTK_TABLE (table_settings),
519               widget,
520               1, 2,
521               n_rows - 1, n_rows,
522               GTK_FILL | GTK_EXPAND, 0,
523               0, 0);
524           gtk_widget_show (widget);
525         }
526       else if (param->dbus_signature[0] == 'b')
527         {
528           widget = gtk_check_button_new_with_label (param_name_formatted);
529           gtk_table_attach (GTK_TABLE (table_settings),
530               widget,
531               0, 2,
532               n_rows - 1, n_rows,
533               GTK_FILL | GTK_EXPAND, 0,
534               0, 0);
535           gtk_widget_show (widget);
536         }
537       else
538         {
539           DEBUG ("Unknown signature for param %s: %s",
540               param_name_formatted, param->dbus_signature);
541         }
542
543       if (widget)
544         account_widget_setup_widget (self, widget, param->name);
545
546       g_free (param_name_formatted);
547     }
548 }
549
550 static void
551 account_widget_handle_params_valist (EmpathyAccountWidget *self,
552     const gchar *first_widget,
553     va_list args)
554 {
555   GObject *object;
556   const gchar *name;
557
558   for (name = first_widget; name; name = va_arg (args, const gchar *))
559     {
560       const gchar *param_name;
561
562       param_name = va_arg (args, const gchar *);
563       object = gtk_builder_get_object (self->ui_details->gui, name);
564
565       if (!object)
566         {
567           g_warning ("Builder is missing object '%s'.", name);
568           continue;
569         }
570
571       account_widget_setup_widget (self, GTK_WIDGET (object), param_name);
572     }
573 }
574
575 static void
576 account_widget_cancel_clicked_cb (GtkWidget *button,
577     EmpathyAccountWidget *self)
578 {
579   g_signal_emit (self, signals[CANCELLED], 0);
580 }
581
582 static void
583 account_widget_account_enabled_cb (GObject *source_object,
584     GAsyncResult *res,
585     gpointer user_data)
586 {
587   GError *error = NULL;
588   EmpathyAccount *account = EMPATHY_ACCOUNT (source_object);
589   EmpathyAccountWidget *widget = EMPATHY_ACCOUNT_WIDGET (user_data);
590
591   empathy_account_set_enabled_finish (account, res, &error);
592
593   if (error != NULL)
594     {
595       DEBUG ("Could not automatically enable new account: %s", error->message);
596       g_error_free (error);
597     }
598   else
599     {
600       g_signal_emit (widget, signals[ACCOUNT_CREATED], 0);
601     }
602 }
603
604 static void
605 account_widget_applied_cb (GObject *source_object,
606     GAsyncResult *res,
607     gpointer user_data)
608 {
609   GError *error = NULL;
610   EmpathyAccount *account;
611   EmpathyAccountSettings *settings = EMPATHY_ACCOUNT_SETTINGS (source_object);
612   EmpathyAccountWidget *widget = EMPATHY_ACCOUNT_WIDGET (user_data);
613   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
614
615   empathy_account_settings_apply_finish (settings, res, &error);
616
617   if (error != NULL)
618     {
619       DEBUG ("Could not apply changes to account: %s", error->message);
620       g_error_free (error);
621       return;
622     }
623
624   account = empathy_account_settings_get_account (priv->settings);
625
626   if (priv->creating_account)
627     {
628       /* By default, when an account is created, we enable it. */
629       empathy_account_set_enabled_async (account, TRUE,
630           account_widget_account_enabled_cb, widget);
631     }
632   else if (account != NULL && priv->enabled_checkbox != NULL)
633     {
634       gboolean enabled_checked;
635
636       enabled_checked = gtk_toggle_button_get_active (
637           GTK_TOGGLE_BUTTON (priv->enabled_checkbox));
638
639       if (empathy_account_is_enabled (account))
640         {
641           /* We want to disable the account (and possibly re-enable it) to make
642            * sure that the new settings are effective */
643           priv->re_enable_accound = enabled_checked;
644           empathy_account_set_enabled_async (account, FALSE, NULL, NULL);
645         }
646       else
647         {
648           /* The account is already disable so we just enable it according
649            * to the value of the "Enabled" checkbox */
650           empathy_account_set_enabled_async (account, enabled_checked,
651               NULL, NULL);
652         }
653     }
654
655   account_widget_set_control_buttons_sensitivity (widget, FALSE);
656 }
657
658 static void
659 account_widget_apply_clicked_cb (GtkWidget *button,
660     EmpathyAccountWidget *self)
661 {
662   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
663
664   empathy_account_settings_apply_async (priv->settings,
665       account_widget_applied_cb, self);
666 }
667
668 static void
669 account_widget_setup_generic (EmpathyAccountWidget *self)
670 {
671   GtkWidget *table_common_settings;
672   GtkWidget *table_advanced_settings;
673
674   table_common_settings = GTK_WIDGET (gtk_builder_get_object
675       (self->ui_details->gui, "table_common_settings"));
676   table_advanced_settings = GTK_WIDGET (gtk_builder_get_object
677       (self->ui_details->gui, "table_advanced_settings"));
678
679   accounts_widget_generic_setup (self, table_common_settings,
680       table_advanced_settings);
681
682   g_object_unref (self->ui_details->gui);
683 }
684
685 static void
686 account_widget_settings_ready_cb (EmpathyAccountSettings *settings,
687     GParamSpec *pspec,
688     gpointer user_data)
689 {
690   EmpathyAccountWidget *self = user_data;
691   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
692
693   if (empathy_account_settings_is_ready (priv->settings))
694     account_widget_setup_generic (self);
695 }
696
697 static void
698 account_widget_build_generic (EmpathyAccountWidget *self,
699     const char *filename)
700 {
701   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
702   GtkWidget *expander_advanced;
703
704   self->ui_details->gui = empathy_builder_get_file (filename,
705       "vbox_generic_settings", &self->ui_details->widget,
706       "expander_advanced_settings", &expander_advanced,
707       NULL);
708
709   if (priv->simple)
710     gtk_widget_hide (expander_advanced);
711
712   g_object_ref (self->ui_details->gui);
713
714   if (empathy_account_settings_is_ready (priv->settings))
715     account_widget_setup_generic (self);
716   else
717     g_signal_connect (priv->settings, "notify::ready",
718         G_CALLBACK (account_widget_settings_ready_cb), self);
719 }
720
721 static void
722 account_widget_build_salut (EmpathyAccountWidget *self,
723     const char *filename)
724 {
725   self->ui_details->gui = empathy_builder_get_file (filename,
726       "vbox_salut_settings", &self->ui_details->widget,
727       NULL);
728
729   empathy_account_widget_handle_params (self,
730       "entry_published", "published-name",
731       "entry_nickname", "nickname",
732       "entry_first_name", "first-name",
733       "entry_last_name", "last-name",
734       "entry_email", "email",
735       "entry_jid", "jid",
736       NULL);
737
738   self->ui_details->default_focus = g_strdup ("entry_nickname");
739 }
740
741 static void
742 account_widget_build_msn (EmpathyAccountWidget *self,
743     const char *filename)
744 {
745   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
746
747   if (priv->simple)
748     {
749       self->ui_details->gui = empathy_builder_get_file (filename,
750           "vbox_msn_simple", &self->ui_details->widget,
751           NULL);
752
753       empathy_account_widget_handle_params (self,
754           "entry_id_simple", "account",
755           "entry_password_simple", "password",
756           NULL);
757
758       self->ui_details->default_focus = g_strdup ("entry_id_simple");
759     }
760   else
761     {
762       self->ui_details->gui = empathy_builder_get_file (filename,
763           "vbox_msn_settings", &self->ui_details->widget,
764           NULL);
765
766       empathy_account_widget_handle_params (self,
767           "entry_id", "account",
768           "entry_password", "password",
769           "entry_server", "server",
770           "spinbutton_port", "port",
771           NULL);
772
773       self->ui_details->default_focus = g_strdup ("entry_id");
774       self->ui_details->add_forget = TRUE;
775     }
776 }
777
778 static void
779 account_widget_build_jabber (EmpathyAccountWidget *self,
780     const char *filename)
781 {
782   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
783   GtkWidget *spinbutton_port;
784   GtkWidget *checkbutton_ssl;
785   GtkWidget *label_id, *label_password;
786   GtkWidget *label_id_create, *label_password_create;
787
788   if (priv->simple)
789     {
790       self->ui_details->gui = empathy_builder_get_file (filename,
791           "vbox_jabber_simple", &self->ui_details->widget,
792           "label_id_simple", &label_id,
793           "label_id_create", &label_id_create,
794           "label_password_simple", &label_password,
795           "label_password_create", &label_password_create,
796           NULL);
797
798       if (empathy_account_settings_get_boolean (priv->settings, "register"))
799         {
800           gtk_widget_hide (label_id);
801           gtk_widget_hide (label_password);
802           gtk_widget_show (label_id_create);
803           gtk_widget_show (label_password_create);
804         }
805
806       empathy_account_widget_handle_params (self,
807           "entry_id_simple", "account",
808           "entry_password_simple", "password",
809           NULL);
810
811       self->ui_details->default_focus = g_strdup ("entry_id_simple");
812     }
813   else
814     {
815       self->ui_details->gui = empathy_builder_get_file (filename,
816           "table_common_settings", &priv->table_common_settings,
817           "vbox_jabber_settings", &self->ui_details->widget,
818           "spinbutton_port", &spinbutton_port,
819           "checkbutton_ssl", &checkbutton_ssl,
820           NULL);
821
822       empathy_account_widget_handle_params (self,
823           "entry_id", "account",
824           "entry_password", "password",
825           "entry_resource", "resource",
826           "entry_server", "server",
827           "spinbutton_port", "port",
828           "spinbutton_priority", "priority",
829           "checkbutton_ssl", "old-ssl",
830           "checkbutton_ignore_ssl_errors", "ignore-ssl-errors",
831           "checkbutton_encryption", "require-encryption",
832           NULL);
833
834       self->ui_details->default_focus = g_strdup ("entry_id");
835       self->ui_details->add_forget = TRUE;
836       priv->spinbutton_port = spinbutton_port;
837
838       g_signal_connect (checkbutton_ssl, "toggled",
839           G_CALLBACK (account_widget_jabber_ssl_toggled_cb),
840           self);
841     }
842 }
843
844 static void
845 account_widget_build_icq (EmpathyAccountWidget *self,
846     const char *filename)
847 {
848   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
849   GtkWidget *spinbutton_port;
850
851   if (priv->simple)
852     {
853       self->ui_details->gui = empathy_builder_get_file (filename,
854           "vbox_icq_simple", &self->ui_details->widget,
855           NULL);
856
857       empathy_account_widget_handle_params (self,
858           "entry_uin_simple", "account",
859           "entry_password_simple", "password",
860           NULL);
861
862       self->ui_details->default_focus = g_strdup ("entry_uin_simple");
863     }
864   else
865     {
866       self->ui_details->gui = empathy_builder_get_file (filename,
867           "vbox_icq_settings", &self->ui_details->widget,
868           "spinbutton_port", &spinbutton_port,
869           NULL);
870
871       empathy_account_widget_handle_params (self,
872           "entry_uin", "account",
873           "entry_password", "password",
874           "entry_server", "server",
875           "spinbutton_port", "port",
876           "entry_charset", "charset",
877           NULL);
878
879       self->ui_details->default_focus = g_strdup ("entry_uin");
880       self->ui_details->add_forget = TRUE;
881     }
882 }
883
884 static void
885 account_widget_build_aim (EmpathyAccountWidget *self,
886     const char *filename)
887 {
888   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
889   GtkWidget *spinbutton_port;
890
891   if (priv->simple)
892     {
893       self->ui_details->gui = empathy_builder_get_file (filename,
894           "vbox_aim_simple", &self->ui_details->widget,
895           NULL);
896
897       empathy_account_widget_handle_params (self,
898           "entry_screenname_simple", "account",
899           "entry_password_simple", "password",
900           NULL);
901
902       self->ui_details->default_focus = g_strdup ("entry_screenname_simple");
903     }
904   else
905     {
906       self->ui_details->gui = empathy_builder_get_file (filename,
907           "vbox_aim_settings", &self->ui_details->widget,
908           "spinbutton_port", &spinbutton_port,
909           NULL);
910
911       empathy_account_widget_handle_params (self,
912           "entry_screenname", "account",
913           "entry_password", "password",
914           "entry_server", "server",
915           "spinbutton_port", "port",
916           NULL);
917
918       self->ui_details->default_focus = g_strdup ("entry_screenname");
919       self->ui_details->add_forget = TRUE;
920     }
921 }
922
923 static void
924 account_widget_build_yahoo (EmpathyAccountWidget *self,
925     const char *filename)
926 {
927   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
928
929   if (priv->simple)
930     {
931       self->ui_details->gui = empathy_builder_get_file (filename,
932           "vbox_yahoo_simple", &self->ui_details->widget,
933           NULL);
934
935       empathy_account_widget_handle_params (self,
936           "entry_id_simple", "account",
937           "entry_password_simple", "password",
938           NULL);
939
940       self->ui_details->default_focus = g_strdup ("entry_id_simple");
941     }
942   else
943     {
944       self->ui_details->gui = empathy_builder_get_file (filename,
945           "vbox_yahoo_settings", &self->ui_details->widget,
946           NULL);
947
948       empathy_account_widget_handle_params (self,
949           "entry_id", "account",
950           "entry_password", "password",
951           "entry_server", "server",
952           "entry_locale", "room-list-locale",
953           "entry_charset", "charset",
954           "spinbutton_port", "port",
955           "checkbutton_yahoojp", "yahoojp",
956           "checkbutton_ignore_invites", "ignore-invites",
957           NULL);
958
959       self->ui_details->default_focus = g_strdup ("entry_id");
960       self->ui_details->add_forget = TRUE;
961     }
962 }
963
964 static void
965 account_widget_build_groupwise (EmpathyAccountWidget *self,
966     const char *filename)
967 {
968   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
969
970   if (priv->simple)
971     {
972       self->ui_details->gui = empathy_builder_get_file (filename,
973           "vbox_groupwise_simple", &self->ui_details->widget,
974           NULL);
975
976       empathy_account_widget_handle_params (self,
977           "entry_id_simple", "account",
978           "entry_password_simple", "password",
979           NULL);
980
981       self->ui_details->default_focus = g_strdup ("entry_id_simple");
982     }
983   else
984     {
985       self->ui_details->gui = empathy_builder_get_file (filename,
986           "vbox_groupwise_settings", &self->ui_details->widget,
987           NULL);
988
989       empathy_account_widget_handle_params (self,
990           "entry_id", "account",
991           "entry_password", "password",
992           "entry_server", "server",
993           "spinbutton_port", "port",
994           NULL);
995
996       self->ui_details->default_focus = g_strdup ("entry_id");
997       self->ui_details->add_forget = TRUE;
998     }
999 }
1000
1001 static void
1002 account_widget_destroy_cb (GtkWidget *widget,
1003     EmpathyAccountWidget *self)
1004 {
1005   g_object_unref (self);
1006 }
1007
1008 static void
1009 empathy_account_widget_enabled_cb (EmpathyAccount *account,
1010       GParamSpec *spec,
1011       gpointer user_data)
1012 {
1013   EmpathyAccountWidget *widget = EMPATHY_ACCOUNT_WIDGET (user_data);
1014   EmpathyAccountWidgetPriv *priv = GET_PRIV (widget);
1015   gboolean enabled = empathy_account_is_enabled (account);
1016
1017   if (!enabled && priv->re_enable_accound)
1018     {
1019       /* The account has been disabled because we were applying changes.
1020        * However, the user wants the account to be enabled so let's re-enable
1021        * it */
1022       empathy_account_set_enabled_async (account, TRUE, NULL, NULL);
1023     }
1024   else if (priv->enabled_checkbox != NULL)
1025     {
1026       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->enabled_checkbox),
1027           enabled);
1028     }
1029 }
1030
1031 static void
1032 account_widget_enabled_toggled_cb (GtkToggleButton *toggle_button,
1033     gpointer user_data)
1034 {
1035   account_widget_handle_control_buttons_sensitivity (
1036       EMPATHY_ACCOUNT_WIDGET (user_data));
1037 }
1038
1039 static void
1040 do_set_property (GObject *object,
1041     guint prop_id,
1042     const GValue *value,
1043     GParamSpec *pspec)
1044 {
1045   EmpathyAccountWidgetPriv *priv = GET_PRIV (object);
1046
1047   switch (prop_id)
1048     {
1049     case PROP_PROTOCOL:
1050       priv->protocol = g_value_dup_string (value);
1051       break;
1052     case PROP_SETTINGS:
1053       priv->settings = g_value_dup_object (value);
1054       break;
1055     case PROP_SIMPLE:
1056       priv->simple = g_value_get_boolean (value);
1057       break;
1058     case PROP_CREATING_ACCOUNT:
1059       priv->creating_account = g_value_get_boolean (value);
1060       break;
1061     default:
1062       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1063     }
1064 }
1065
1066 static void
1067 do_get_property (GObject *object,
1068     guint prop_id,
1069     GValue *value,
1070     GParamSpec *pspec)
1071 {
1072   EmpathyAccountWidgetPriv *priv = GET_PRIV (object);
1073
1074   switch (prop_id)
1075     {
1076     case PROP_PROTOCOL:
1077       g_value_set_string (value, priv->protocol);
1078       break;
1079     case PROP_SETTINGS:
1080       g_value_set_object (value, priv->settings);
1081       break;
1082     case PROP_SIMPLE:
1083       g_value_set_boolean (value, priv->simple);
1084       break;
1085     case PROP_CREATING_ACCOUNT:
1086       g_value_set_boolean (value, priv->creating_account);
1087       break;
1088     default:
1089       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1090     }
1091 }
1092
1093 static void
1094 do_constructed (GObject *obj)
1095 {
1096   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
1097   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1098   EmpathyAccount *account;
1099   char *uiname, *filename;
1100
1101   uiname = g_strconcat ("empathy-account-widget-", priv->protocol,
1102       ".ui", NULL);
1103   filename = empathy_file_lookup (uiname, "libempathy-gtk");
1104
1105   if (!tp_strdiff (priv->protocol, "local-xmpp"))
1106     account_widget_build_salut (self, filename);
1107   else if (!tp_strdiff (priv->protocol, "msn"))
1108     account_widget_build_msn (self, filename);
1109   else if (!tp_strdiff (priv->protocol, "jabber"))
1110     account_widget_build_jabber (self, filename);
1111   else if (!tp_strdiff (priv->protocol, "icq"))
1112     account_widget_build_icq (self, filename);
1113   else if (!tp_strdiff (priv->protocol, "aim"))
1114     account_widget_build_aim (self, filename);
1115   else if (!tp_strdiff (priv->protocol, "yahoo"))
1116     account_widget_build_yahoo (self, filename);
1117   else if (!tp_strdiff (priv->protocol, "groupwise"))
1118     account_widget_build_groupwise (self, filename);
1119   else if (!tp_strdiff (priv->protocol, "irc"))
1120     empathy_account_widget_irc_build (self, filename);
1121   else if (!tp_strdiff (priv->protocol, "sip"))
1122     empathy_account_widget_sip_build (self, filename);
1123   else
1124     {
1125       g_free (filename);
1126
1127       filename = empathy_file_lookup (
1128           "empathy-account-widget-generic.ui", "libempathy-gtk");
1129       account_widget_build_generic (self, filename);
1130     }
1131
1132   g_free (uiname);
1133   g_free (filename);
1134
1135   /* handle default focus */
1136   if (self->ui_details->default_focus != NULL)
1137     {
1138       GObject *default_focus_entry;
1139
1140       default_focus_entry = gtk_builder_get_object
1141         (self->ui_details->gui, self->ui_details->default_focus);
1142       g_signal_connect (default_focus_entry, "realize",
1143           G_CALLBACK (gtk_widget_grab_focus),
1144           NULL);
1145     }
1146
1147   /* handle forget button */
1148   if (self->ui_details->add_forget)
1149     {
1150       const gchar *password = NULL;
1151
1152       priv->button_forget = GTK_WIDGET (gtk_builder_get_object
1153           (self->ui_details->gui, "button_forget"));
1154       priv->entry_password = GTK_WIDGET (gtk_builder_get_object
1155           (self->ui_details->gui, "entry_password"));
1156
1157       password = empathy_account_settings_get_string (priv->settings,
1158           "password");
1159       gtk_widget_set_sensitive (priv->button_forget,
1160           !EMP_STR_EMPTY (password));
1161
1162       g_signal_connect (priv->button_forget, "clicked",
1163           G_CALLBACK (account_widget_forget_clicked_cb),
1164           self);
1165       g_signal_connect (priv->entry_password, "changed",
1166           G_CALLBACK (account_widget_password_changed_cb),
1167           self);
1168     }
1169
1170   /* handle apply and cancel button */
1171   if (!priv->simple)
1172     {
1173       GtkWidget *hbox = gtk_hbox_new (TRUE, 3);
1174
1175       priv->cancel_button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
1176       priv->apply_button = gtk_button_new_from_stock (GTK_STOCK_APPLY);
1177
1178       gtk_box_pack_end (GTK_BOX (hbox), priv->apply_button, TRUE,
1179           TRUE, 3);
1180       gtk_box_pack_end (GTK_BOX (hbox), priv->cancel_button, TRUE,
1181           TRUE, 3);
1182
1183       gtk_box_pack_end (GTK_BOX (self->ui_details->widget), hbox, FALSE,
1184           FALSE, 3);
1185
1186       g_signal_connect (priv->cancel_button, "clicked",
1187           G_CALLBACK (account_widget_cancel_clicked_cb),
1188           self);
1189       g_signal_connect (priv->apply_button, "clicked",
1190           G_CALLBACK (account_widget_apply_clicked_cb),
1191           self);
1192       gtk_widget_show_all (hbox);
1193       account_widget_set_control_buttons_sensitivity (self, FALSE);
1194     }
1195
1196   account = empathy_account_settings_get_account (priv->settings);
1197
1198   if (account != NULL)
1199     {
1200       g_signal_connect (account, "notify::enabled",
1201           G_CALLBACK (empathy_account_widget_enabled_cb), self);
1202     }
1203
1204   /* handle the "Enabled" checkbox. We only add it when modifying an account */
1205   if (!priv->creating_account && priv->table_common_settings != NULL)
1206     {
1207       guint nb_rows, nb_columns;
1208
1209       priv->enabled_checkbox =
1210           gtk_check_button_new_with_label (_("Enabled"));
1211       gtk_toggle_button_set_active (
1212           GTK_TOGGLE_BUTTON (priv->enabled_checkbox),
1213           empathy_account_is_enabled (account));
1214
1215       g_object_get (priv->table_common_settings, "n-rows", &nb_rows,
1216           "n-columns", &nb_columns, NULL);
1217
1218       gtk_table_resize (GTK_TABLE (priv->table_common_settings), ++nb_rows,
1219           nb_columns);
1220
1221       gtk_table_attach_defaults (GTK_TABLE (priv->table_common_settings),
1222           priv->enabled_checkbox, 0, nb_columns, nb_rows - 1, nb_rows);
1223
1224       gtk_widget_show (priv->enabled_checkbox);
1225
1226       g_signal_connect (G_OBJECT (priv->enabled_checkbox), "toggled",
1227           G_CALLBACK (account_widget_enabled_toggled_cb), self);
1228     }
1229
1230   /* hook up to widget destruction to unref ourselves */
1231   g_signal_connect (self->ui_details->widget, "destroy",
1232       G_CALLBACK (account_widget_destroy_cb), self);
1233
1234   empathy_builder_unref_and_keep_widget (self->ui_details->gui,
1235       self->ui_details->widget);
1236   self->ui_details->gui = NULL;
1237 }
1238
1239 static void
1240 do_dispose (GObject *obj)
1241 {
1242   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
1243   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1244
1245   if (priv->dispose_run)
1246     return;
1247
1248   priv->dispose_run = TRUE;
1249
1250   empathy_account_settings_is_ready (priv->settings);
1251
1252   if (priv->settings != NULL)
1253     {
1254       EmpathyAccount *account;
1255       account = empathy_account_settings_get_account (priv->settings);
1256
1257       if (account != NULL)
1258         {
1259           g_signal_handlers_disconnect_by_func (account,
1260               empathy_account_widget_enabled_cb, self);
1261         }
1262
1263       g_object_unref (priv->settings);
1264       priv->settings = NULL;
1265     }
1266
1267   if (G_OBJECT_CLASS (empathy_account_widget_parent_class)->dispose != NULL)
1268     G_OBJECT_CLASS (empathy_account_widget_parent_class)->dispose (obj);
1269 }
1270
1271 static void
1272 do_finalize (GObject *obj)
1273 {
1274   EmpathyAccountWidget *self = EMPATHY_ACCOUNT_WIDGET (obj);
1275   EmpathyAccountWidgetPriv *priv = GET_PRIV (self);
1276
1277   g_free (self->ui_details->default_focus);
1278   g_slice_free (EmpathyAccountWidgetUIDetails, self->ui_details);
1279
1280   g_free (priv->protocol);
1281
1282   if (G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize != NULL)
1283     G_OBJECT_CLASS (empathy_account_widget_parent_class)->finalize (obj);
1284 }
1285
1286 static void
1287 empathy_account_widget_class_init (EmpathyAccountWidgetClass *klass)
1288 {
1289   GObjectClass *oclass = G_OBJECT_CLASS (klass);
1290   GParamSpec *param_spec;
1291
1292   oclass->get_property = do_get_property;
1293   oclass->set_property = do_set_property;
1294   oclass->constructed = do_constructed;
1295   oclass->dispose = do_dispose;
1296   oclass->finalize = do_finalize;
1297
1298   param_spec = g_param_spec_string ("protocol",
1299       "protocol", "The protocol of the account",
1300       NULL,
1301       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
1302   g_object_class_install_property (oclass, PROP_PROTOCOL, param_spec);
1303
1304   param_spec = g_param_spec_object ("settings",
1305       "settings", "The settings of the account",
1306       EMPATHY_TYPE_ACCOUNT_SETTINGS,
1307       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
1308   g_object_class_install_property (oclass, PROP_SETTINGS, param_spec);
1309
1310   param_spec = g_param_spec_boolean ("simple",
1311       "simple", "Whether the account widget is a simple or an advanced one",
1312       FALSE,
1313       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
1314   g_object_class_install_property (oclass, PROP_SIMPLE, param_spec);
1315
1316   param_spec = g_param_spec_boolean ("creating-account",
1317       "creating-account",
1318       "TRUE if we're creating an account, FALSE if we're modifying it",
1319       FALSE,
1320       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);
1321   g_object_class_install_property (oclass, PROP_CREATING_ACCOUNT, param_spec);
1322
1323   signals[HANDLE_APPLY] =
1324     g_signal_new ("handle-apply", G_TYPE_FROM_CLASS (klass),
1325         G_SIGNAL_RUN_LAST, 0, NULL, NULL,
1326         g_cclosure_marshal_VOID__BOOLEAN,
1327         G_TYPE_NONE,
1328         1, G_TYPE_BOOLEAN);
1329
1330   /* This signal is emitted when an account has been created and enabled. */
1331   signals[ACCOUNT_CREATED] =
1332       g_signal_new ("account-created", G_TYPE_FROM_CLASS (klass),
1333           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
1334           g_cclosure_marshal_VOID__VOID,
1335           G_TYPE_NONE,
1336           0);
1337
1338   signals[CANCELLED] =
1339       g_signal_new ("cancelled", G_TYPE_FROM_CLASS (klass),
1340           G_SIGNAL_RUN_LAST, 0, NULL, NULL,
1341           g_cclosure_marshal_VOID__VOID,
1342           G_TYPE_NONE,
1343           0);
1344
1345   g_type_class_add_private (klass, sizeof (EmpathyAccountWidgetPriv));
1346 }
1347
1348 static void
1349 empathy_account_widget_init (EmpathyAccountWidget *self)
1350 {
1351   EmpathyAccountWidgetPriv *priv =
1352     G_TYPE_INSTANCE_GET_PRIVATE ((self), EMPATHY_TYPE_ACCOUNT_WIDGET,
1353         EmpathyAccountWidgetPriv);
1354
1355   self->priv = priv;
1356   priv->dispose_run = FALSE;
1357
1358   self->ui_details = g_slice_new0 (EmpathyAccountWidgetUIDetails);
1359 }
1360
1361 /* public methods */
1362
1363 void
1364 empathy_account_widget_handle_params (EmpathyAccountWidget *self,
1365     const gchar *first_widget,
1366     ...)
1367 {
1368   va_list args;
1369
1370   va_start (args, first_widget);
1371   account_widget_handle_params_valist (self, first_widget, args);
1372   va_end (args);
1373 }
1374
1375 GtkWidget *
1376 empathy_account_widget_get_widget (EmpathyAccountWidget *widget)
1377 {
1378   return widget->ui_details->widget;
1379 }
1380
1381 EmpathyAccountWidget *
1382 empathy_account_widget_new_for_protocol (const char *protocol,
1383     EmpathyAccountSettings *settings,
1384     gboolean simple)
1385 {
1386   EmpathyAccountWidget *self;
1387
1388   g_return_val_if_fail (EMPATHY_IS_ACCOUNT_SETTINGS (settings), NULL);
1389   g_return_val_if_fail (protocol != NULL, NULL);
1390
1391   self = g_object_new
1392     (EMPATHY_TYPE_ACCOUNT_WIDGET, "protocol", protocol,
1393         "settings", settings, "simple", simple,
1394         "creating-account",
1395         empathy_account_settings_get_account (settings) == NULL,
1396         NULL);
1397
1398   return self;
1399 }