Modified webpage: now tinymail repository is in gitorious.
[modest] / src / hildon2 / modest-number-editor.c
1 /*
2  * Copyright (C) 2008 Nokia Corporation, all rights reserved.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright
12  *   notice, this list of conditions and the following disclaimer in the
13  *   documentation and/or other materials provided with the distribution.
14  * * Neither the name of the Nokia Corporation nor the names of its
15  *   contributors may be used to endorse or promote products derived from
16  *   this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
21  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 /**
32  * SECTION:modest-number-editor
33  * @short_description: A widget used to enter a number within a pre-defined range.
34  *
35  * ModestNumberEditor is used to enter a number from a specific range. 
36  * There are two buttons to scroll the value in number field. 
37  * Manual input is also possible.
38  *
39  * <example>
40  * <title>ModestNumberEditor example</title>
41  * <programlisting>
42  * number_editor = modest_number_editor_new (-250, 500);
43  * modest_number_editor_set_range (number_editor, 0, 100);
44  * </programlisting>
45  * </example>
46  */
47
48 #undef                                          MODEST_DISABLE_DEPRECATED
49
50 #ifdef                                          HAVE_CONFIG_H
51 #include                                        <config.h>
52 #endif
53
54 #include                                        <string.h>
55 #include                                        <stdio.h>
56 #include                                        <stdlib.h>
57 #include                                        <libintl.h>
58 #include                                        <gdk/gdkkeysyms.h>
59
60 #include                                        "modest-number-editor.h"
61 #include                                        "modest-marshal.h"
62 #include                                        <hildon/hildon-banner.h>
63 #include                                        "modest-text-utils.h"
64 #include                                        "modest-platform.h"
65
66 typedef struct                                  _ModestNumberEditorPrivate ModestNumberEditorPrivate;
67
68 #define                                         MODEST_NUMBER_EDITOR_GET_PRIVATE(obj) \
69                                                 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MODEST_TYPE_NUMBER_EDITOR, \
70                                                 ModestNumberEditorPrivate));
71
72 struct                                          _ModestNumberEditorPrivate
73 {
74         gint start; /* Minimum */
75         gint end;   /* Maximum */
76         gint default_val;
77         gboolean is_valid;
78
79         /* Timer IDs */
80         guint select_all_idle_id; /* Selection repaint hack
81                                      see modest_number_editor_select_all */
82 };
83
84
85 static void
86 modest_number_editor_class_init                 (ModestNumberEditorClass *editor_class);
87
88 static void
89 modest_number_editor_init                       (ModestNumberEditor *editor);
90
91 static gboolean
92 modest_number_editor_entry_focusout             (GtkWidget *widget, 
93                                                  GdkEventFocus *event,
94                                                  gpointer data);
95
96 static void
97 modest_number_editor_entry_changed              (GtkWidget *widget, 
98                                                  gpointer data);
99
100 static void
101 modest_number_editor_finalize                   (GObject *self);
102
103 static gboolean
104 modest_number_editor_range_error                (ModestNumberEditor *editor,
105                                                  ModestNumberEditorErrorType type);
106
107 static gboolean
108 modest_number_editor_select_all                 (ModestNumberEditor *editor);
109
110 static void
111 modest_number_editor_validate_value             (ModestNumberEditor *editor, 
112                                                  gboolean allow_intermediate);
113     
114 static void 
115 modest_number_editor_set_property               (GObject * object,
116                                                  guint prop_id,
117                                                  const GValue * value,
118                                                  GParamSpec * pspec);
119
120 static void
121 modest_number_editor_get_property               (GObject *object,
122                                                  guint prop_id,
123                                                  GValue *value, 
124                                                  GParamSpec * pspec);
125
126 enum
127 {
128     RANGE_ERROR,
129     VALID_CHANGED,
130     LAST_SIGNAL
131 };
132
133 enum {
134     PROP_0,
135     PROP_VALUE
136 };
137
138 static GtkContainerClass*                       parent_class;
139
140 static guint                                    ModestNumberEditor_signal[LAST_SIGNAL] = {0};
141
142 /**
143  * modest_number_editor_get_type:
144  *
145  * Returns GType for ModestNumberEditor.
146  *
147  * Returns: ModestNumberEditor type
148  */
149 GType G_GNUC_CONST
150 modest_number_editor_get_type                   (void)
151 {
152     static GType editor_type = 0;
153
154     if (!editor_type)
155     {
156         static const GTypeInfo editor_info =
157         {
158             sizeof (ModestNumberEditorClass),
159             NULL,       /* base_init */
160             NULL,       /* base_finalize */
161             (GClassInitFunc) modest_number_editor_class_init,
162             NULL,       /* class_finalize */
163             NULL,       /* class_data */
164             sizeof (ModestNumberEditor),
165             0,  /* n_preallocs */
166             (GInstanceInitFunc) modest_number_editor_init,
167         };
168         editor_type = g_type_register_static (HILDON_TYPE_ENTRY,
169                 "ModestNumberEditor",
170                 &editor_info, 0);
171     }
172     return editor_type;
173 }
174
175 static void
176 modest_number_editor_class_init                 (ModestNumberEditorClass *editor_class)
177 {
178     GObjectClass *gobject_class = G_OBJECT_CLASS (editor_class);
179
180     g_type_class_add_private (editor_class,
181             sizeof (ModestNumberEditorPrivate));
182
183     parent_class = g_type_class_peek_parent (editor_class);
184
185     editor_class->range_error = modest_number_editor_range_error;
186
187     gobject_class->finalize                 = modest_number_editor_finalize;
188     gobject_class->set_property             = modest_number_editor_set_property;
189     gobject_class->get_property             = modest_number_editor_get_property;
190
191     /**
192      * ModestNumberEditor:value:
193      *
194      * The current value of the number editor.
195      */
196     g_object_class_install_property (gobject_class, PROP_VALUE,
197             g_param_spec_int ("value",
198                 "Value",
199                 "The current value of number editor",
200                 G_MININT,
201                 G_MAXINT,
202                 0, G_PARAM_READWRITE));
203
204     ModestNumberEditor_signal[RANGE_ERROR] =
205         g_signal_new ("range_error", MODEST_TYPE_NUMBER_EDITOR,
206                 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET
207                 (ModestNumberEditorClass, range_error),
208                 g_signal_accumulator_true_handled, NULL,
209                 modest_marshal_BOOLEAN__ENUM,
210                 G_TYPE_BOOLEAN, 1, MODEST_TYPE_NUMBER_EDITOR_ERROR_TYPE);
211
212     ModestNumberEditor_signal[VALID_CHANGED] =
213         g_signal_new ("valid_changed", MODEST_TYPE_NUMBER_EDITOR,
214                 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET
215                 (ModestNumberEditorClass, valid_changed),
216                 NULL, NULL,
217                 g_cclosure_marshal_VOID__BOOLEAN,
218                 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
219 }
220
221 static void
222 modest_number_editor_finalize                   (GObject *self)
223 {
224     ModestNumberEditorPrivate *priv;
225
226     priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (self);
227     g_assert (priv);
228
229     if (priv->select_all_idle_id)
230         g_source_remove (priv->select_all_idle_id);
231
232     /* Call parent class finalize, if have one */
233     if (G_OBJECT_CLASS (parent_class)->finalize)
234         G_OBJECT_CLASS (parent_class)->finalize(self);
235 }
236
237 static void
238 modest_number_editor_init                       (ModestNumberEditor *editor)
239 {
240     ModestNumberEditorPrivate *priv;
241
242     priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
243     g_assert (priv);
244
245     priv->select_all_idle_id = 0;
246     priv->is_valid = TRUE;
247 }
248
249 /* Format given number to editor field, no checks performed, all signals
250    are sent normally. */
251 static void
252 modest_number_editor_real_set_value             (ModestNumberEditor *editor, 
253                                                  gint value)
254 {
255     /* FIXME: That looks REALLY bad */
256     gchar buffer[32];
257
258     /* Update text in entry to new value */
259     g_snprintf (buffer, sizeof (buffer), "%d", value);
260     gtk_entry_set_text (GTK_ENTRY (editor), buffer);
261 }
262
263 static void
264 add_select_all_idle                             (ModestNumberEditor *editor)
265 {
266     ModestNumberEditorPrivate *priv;
267
268     priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
269
270     if (! priv->select_all_idle_id)
271     {
272         priv->select_all_idle_id =
273             g_idle_add((GSourceFunc) modest_number_editor_select_all, editor);
274     }    
275 }
276
277 static void
278 modest_number_editor_validate_value             (ModestNumberEditor *editor, 
279                                                  gboolean allow_intermediate)
280 {
281         ModestNumberEditorPrivate *priv;
282         gint error_code, fixup_value;
283         const gchar *text;
284         long value;
285         gchar *tail;
286         gboolean r;
287         gboolean is_valid = TRUE;
288
289         g_assert (MODEST_IS_NUMBER_EDITOR(editor));
290         
291         priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
292         g_assert (priv);
293
294         text = gtk_entry_get_text (GTK_ENTRY (editor));
295         error_code = -1;
296         fixup_value = priv->default_val;
297
298         if (text && text[0]) {
299                 /* Try to convert entry text to number */
300                 value = strtol (text, &tail, 10);
301
302                 /* Check if conversion succeeded */
303                 if (tail[0] == 0) {
304                         /* Check if value is in allowed range. This is tricky in those
305                            cases when user is editing a value. 
306                            For example: Range = [100, 500] and user have just inputted "4".
307                            This should not lead into error message. Otherwise value is
308                            resetted back to "100" and next "4" press will reset it back
309                            and so on. */
310
311                         if (allow_intermediate) {
312                                 /* We now have the following error cases:
313                                  * If inputted value as above maximum and
314                                  maximum is either positive or then maximum
315                                  negative and value is positive.
316                                  * If inputted value is below minimum and minimum
317                                  is negative or minumum positive and value
318                                  negative or zero.
319                                  In all other cases situation can be fixed just by
320                                  adding new numbers to the string.
321                                 */
322                                 if (value > priv->end && (priv->end >= 0 || (priv->end < 0 && value >= 0))) {
323                                         error_code = MODEST_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED;
324                                         fixup_value = priv->end;
325                                         is_valid = FALSE;
326                                 } else if (value < priv->start && (priv->start < 0 || (priv->start >= 0 && value <= 0))) {
327                                         error_code = MODEST_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED;
328                                         fixup_value = priv->start;
329                                         is_valid = FALSE;
330                                 }
331                         } else {
332                                 if (value > priv->end) {
333                                         error_code = MODEST_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED;
334                                         fixup_value = priv->end;
335                                         is_valid = FALSE;
336                                 } else if (value < priv->start) {
337                                         error_code = MODEST_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED;
338                                         fixup_value = priv->start;
339                                         is_valid = FALSE;
340                                 }
341                         }
342                         /* The only valid case when conversion can fail is when we
343                            have plain '-', intermediate forms are allowed AND
344                            minimum bound is negative */
345                 } else {
346                         is_valid = FALSE;
347                         if (! allow_intermediate || strcmp (text, "-") != 0 || priv->start >= 0)
348                                 error_code = MODEST_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE;
349                 }
350         } else {
351                 is_valid = FALSE;
352                 if (! allow_intermediate)
353                         error_code = MODEST_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE;
354         }
355
356         if (error_code != -1) {
357                 /* If entry is empty and intermediate forms are nor allowed, 
358                    emit error signal */
359                 /* Change to default value */
360                 modest_number_editor_set_value (editor, fixup_value);
361                 g_signal_emit (editor, ModestNumberEditor_signal[RANGE_ERROR], 0, error_code, &r);
362                 add_select_all_idle (editor);
363                 is_valid = modest_number_editor_is_valid (editor);
364         }
365
366         if (priv->is_valid != is_valid) {
367                 priv->is_valid = is_valid;
368                 g_signal_emit (editor, ModestNumberEditor_signal[VALID_CHANGED], 0, is_valid);
369         }
370 }
371
372 static void 
373 modest_number_editor_entry_changed              (GtkWidget *widget, 
374                                                  gpointer data)
375 {
376     g_assert (MODEST_IS_NUMBER_EDITOR (data));
377     modest_number_editor_validate_value (MODEST_NUMBER_EDITOR (data), TRUE);
378     g_object_notify (G_OBJECT (data), "value");
379 }
380
381 static gboolean
382 modest_number_editor_entry_focusout             (GtkWidget *widget, 
383                                                  GdkEventFocus *event,
384                                                  gpointer data)
385 {
386         GtkWidget *window;
387         
388         g_assert (MODEST_IS_NUMBER_EDITOR(data));
389
390         window = gtk_widget_get_toplevel (widget);
391         if (window && gtk_window_has_toplevel_focus (GTK_WINDOW (window)))
392                 modest_number_editor_validate_value (MODEST_NUMBER_EDITOR(data), FALSE);
393
394         return FALSE;
395 }
396
397 static gboolean
398 modest_number_editor_range_error                (ModestNumberEditor *editor,
399                                                  ModestNumberEditorErrorType type)
400 {
401
402     gint min, max;
403     gchar *err_msg = NULL;
404     ModestNumberEditorPrivate *priv;
405
406     priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
407     g_assert (priv);
408
409     min = priv->start;
410     max = priv->end;
411
412     /* Construct error message */
413     switch (type)
414     {
415         case MODEST_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED:
416             err_msg = g_strdup_printf (_HL("ckct_ib_maximum_value"), max, max);
417             break;
418
419         case MODEST_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED:
420             err_msg = g_strdup_printf (_HL("ckct_ib_minimum_value"), min, min);
421             break;
422
423         case MODEST_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE:
424             err_msg =
425                 g_strdup_printf (_HL("ckct_ib_set_a_value_within_range"), min, max);
426             break;
427     }
428
429     /* Infoprint error */
430     if (err_msg) {
431         modest_platform_system_banner (GTK_WIDGET (GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET(editor),
432                                         GTK_TYPE_WINDOW))), NULL, err_msg);
433         g_free(err_msg);
434     }
435
436     return TRUE;
437 }
438
439 /**
440  * modest_number_editor_new:
441  * @min: minimum accepted value
442  * @max: maximum accepted value
443  * 
444  * Creates new number editor
445  *
446  * Returns: a new #ModestNumberEditor widget
447  */
448 GtkWidget*
449 modest_number_editor_new                        (gint min, 
450                                                  gint max)
451 {
452     ModestNumberEditor *editor = g_object_new (MODEST_TYPE_NUMBER_EDITOR, NULL);
453
454     /* Connect child widget signals */
455     g_signal_connect (GTK_OBJECT (editor), "changed",
456             G_CALLBACK (modest_number_editor_entry_changed),
457             editor);
458
459     g_signal_connect (GTK_OBJECT (editor), "focus-out-event",
460             G_CALLBACK (modest_number_editor_entry_focusout),
461             editor);
462
463     /* Numeric input mode */
464     hildon_gtk_entry_set_input_mode (GTK_ENTRY (editor), 
465                                      HILDON_GTK_INPUT_MODE_NUMERIC);
466     hildon_gtk_widget_set_theme_size ((GtkWidget *) editor, 
467                                       HILDON_SIZE_FINGER_HEIGHT);
468
469     /* Set user inputted range to editor */
470     modest_number_editor_set_range (editor, min, max);
471
472     return GTK_WIDGET (editor);
473 }
474
475 /**
476  * modest_number_editor_set_range:
477  * @editor: a #ModestNumberEditor widget
478  * @min: minimum accepted value
479  * @max: maximum accepted value
480  *
481  * Sets accepted number range for editor
482  */
483 void
484 modest_number_editor_set_range                  (ModestNumberEditor *editor, 
485                                                  gint min, 
486                                                  gint max)
487 {
488     ModestNumberEditorPrivate *priv;
489     gchar buffer_min[32], buffer_max[32];
490     gint a, b;
491
492     g_return_if_fail (MODEST_IS_NUMBER_EDITOR (editor));
493
494     priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
495     g_assert (priv);
496
497     /* Set preferences */
498     priv->start = MIN (min, max);
499     priv->end = MAX (min, max);
500
501     /* Find maximum allowed length of value */
502     g_snprintf (buffer_min, sizeof (buffer_min), "%d", min);
503     g_snprintf (buffer_max, sizeof (buffer_max), "%d", max);
504     a = strlen (buffer_min);
505     b = strlen (buffer_max);
506
507     /* Set maximum size of entry */
508     gtk_entry_set_width_chars (GTK_ENTRY (editor), MAX (a, b));
509     modest_number_editor_set_value (editor, priv->start);
510 }
511
512 /**
513  * modest_number_editor_is_valid:
514  * @editor: pointer to #ModestNumberEditor
515  *
516  * Returns: if @editor contents are valid
517  */
518 gboolean
519 modest_number_editor_is_valid                  (ModestNumberEditor *editor)
520 {
521     ModestNumberEditorPrivate *priv;
522
523     g_return_val_if_fail (MODEST_IS_NUMBER_EDITOR (editor), FALSE);
524
525     priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
526     g_assert (priv);
527
528     return priv->is_valid;
529
530 }
531
532 /**
533  * modest_number_editor_get_value:
534  * @editor: pointer to #ModestNumberEditor
535  *
536  * Returns: current NumberEditor value
537  */
538 gint
539 modest_number_editor_get_value                  (ModestNumberEditor *editor)
540 {
541     ModestNumberEditorPrivate *priv;
542
543     g_return_val_if_fail (MODEST_IS_NUMBER_EDITOR (editor), 0);
544
545     priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
546     g_assert (priv);
547
548     return atoi (gtk_entry_get_text (GTK_ENTRY (editor)));
549 }
550
551 /**
552  * modest_number_editor_set_value:
553  * @editor: pointer to #ModestNumberEditor
554  * @value: numeric value for number editor
555  *
556  * Sets numeric value for number editor
557  */
558 void
559 modest_number_editor_set_value                  (ModestNumberEditor *editor, 
560                                                  gint value)
561 {
562     ModestNumberEditorPrivate *priv;
563
564     g_return_if_fail (MODEST_IS_NUMBER_EDITOR (editor));
565
566     priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
567     g_assert (priv);
568
569     g_return_if_fail (value <= priv->end);
570     g_return_if_fail (value >= priv->start);
571
572     priv->default_val = value;
573     modest_number_editor_real_set_value (editor, value);
574     g_object_notify (G_OBJECT(editor), "value");
575 }
576
577 /* When calling gtk_entry_set_text, the entry widget does things that can
578  * cause the whole widget to redraw. This redrawing is delayed and if any
579  * selections are made right after calling the gtk_entry_set_text the
580  * setting of the selection might seem to have no effect.
581  *
582  * If the selection is delayed with a lower priority than the redrawing,
583  * the selection should stick. Calling this function with g_idle_add should
584  * do it.
585  */
586 static gboolean
587 modest_number_editor_select_all                 (ModestNumberEditor *editor)
588 {   
589     ModestNumberEditorPrivate *priv;
590
591     g_return_val_if_fail (MODEST_IS_NUMBER_EDITOR (editor), FALSE);
592
593     priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
594     g_assert (priv);
595
596     GDK_THREADS_ENTER ();
597     gtk_editable_select_region (GTK_EDITABLE (editor), 0, -1);
598     priv->select_all_idle_id = 0;
599     GDK_THREADS_LEAVE ();
600     return FALSE;
601
602
603 static void
604 modest_number_editor_set_property               (GObject *object,
605                                                  guint prop_id,
606                                                  const GValue *value, 
607                                                  GParamSpec *pspec)
608 {
609     ModestNumberEditor *editor;
610
611     editor = MODEST_NUMBER_EDITOR (object);
612
613     switch (prop_id) {
614
615         case PROP_VALUE:
616             modest_number_editor_set_value (editor, g_value_get_int (value));
617             break;
618
619         default:
620             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
621             break;
622     }
623 }
624
625 static void
626 modest_number_editor_get_property               (GObject *object,
627                                                  guint prop_id, 
628                                                  GValue *value, 
629                                                  GParamSpec *pspec)
630 {
631     ModestNumberEditor *editor;
632
633     editor = MODEST_NUMBER_EDITOR (object);
634
635     switch (prop_id) {
636
637         case PROP_VALUE:
638             g_value_set_int(value, modest_number_editor_get_value (editor));
639             break;
640
641         default:
642             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
643             break;
644     }
645 }
646
647
648 /* enumerations from "modest-number-editor.h" */
649 GType
650 modest_number_editor_error_type_get_type (void)
651 {
652   static GType etype = 0;
653   if (etype == 0) {
654     static const GEnumValue values[] = {
655       { MODEST_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED, "MODEST_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED", "maximum-value-exceed" },
656       { MODEST_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED, "MODEST_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED", "minimum-value-exceed" },
657       { MODEST_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE, "MODEST_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE", "erroneous-value" },
658       { 0, NULL, NULL }
659     };
660     etype = g_enum_register_static ("ModestNumberEditorErrorType", values);
661   }
662   return etype;
663 }
664