2 * This file is a part of modest
4 * Copyright (C) 2005, 2006, 2008 Nokia Corporation, all rights reserved.
9 * SECTION:modest-number-editor
10 * @short_description: A widget used to enter a number within a pre-defined range.
12 * ModestNumberEditor is used to enter a number from a specific range.
13 * There are two buttons to scroll the value in number field.
14 * Manual input is also possible.
17 * <title>ModestNumberEditor example</title>
19 * number_editor = modest_number_editor_new (-250, 500);
20 * modest_number_editor_set_range (number_editor, 0, 100);
25 #undef MODEST_DISABLE_DEPRECATED
35 #include <gdk/gdkkeysyms.h>
37 #include "modest-number-editor.h"
38 #include "modest-marshal.h"
39 #include <hildon/hildon-banner.h>
40 #include "modest-text-utils.h"
42 #define _(String) dgettext("modest-libs", String)
44 typedef struct _ModestNumberEditorPrivate ModestNumberEditorPrivate;
46 #define MODEST_NUMBER_EDITOR_GET_PRIVATE(obj) \
47 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MODEST_TYPE_NUMBER_EDITOR, \
48 ModestNumberEditorPrivate));
50 struct _ModestNumberEditorPrivate
52 gint start; /* Minimum */
53 gint end; /* Maximum */
58 guint select_all_idle_id; /* Selection repaint hack
59 see modest_number_editor_select_all */
64 modest_number_editor_class_init (ModestNumberEditorClass *editor_class);
67 modest_number_editor_init (ModestNumberEditor *editor);
70 modest_number_editor_entry_focusout (GtkWidget *widget,
75 modest_number_editor_entry_changed (GtkWidget *widget,
79 modest_number_editor_finalize (GObject *self);
82 modest_number_editor_range_error (ModestNumberEditor *editor,
83 ModestNumberEditorErrorType type);
86 modest_number_editor_select_all (ModestNumberEditor *editor);
89 modest_number_editor_validate_value (ModestNumberEditor *editor,
90 gboolean allow_intermediate);
93 modest_number_editor_set_property (GObject * object,
99 modest_number_editor_get_property (GObject *object,
116 static GtkContainerClass* parent_class;
118 static guint ModestNumberEditor_signal[LAST_SIGNAL] = {0};
121 * modest_number_editor_get_type:
123 * Returns GType for ModestNumberEditor.
125 * Returns: ModestNumberEditor type
128 modest_number_editor_get_type (void)
130 static GType editor_type = 0;
134 static const GTypeInfo editor_info =
136 sizeof (ModestNumberEditorClass),
137 NULL, /* base_init */
138 NULL, /* base_finalize */
139 (GClassInitFunc) modest_number_editor_class_init,
140 NULL, /* class_finalize */
141 NULL, /* class_data */
142 sizeof (ModestNumberEditor),
144 (GInstanceInitFunc) modest_number_editor_init,
146 editor_type = g_type_register_static (HILDON_TYPE_ENTRY,
147 "ModestNumberEditor",
154 modest_number_editor_class_init (ModestNumberEditorClass *editor_class)
156 GObjectClass *gobject_class = G_OBJECT_CLASS (editor_class);
158 g_type_class_add_private (editor_class,
159 sizeof (ModestNumberEditorPrivate));
161 parent_class = g_type_class_peek_parent (editor_class);
163 editor_class->range_error = modest_number_editor_range_error;
165 gobject_class->finalize = modest_number_editor_finalize;
166 gobject_class->set_property = modest_number_editor_set_property;
167 gobject_class->get_property = modest_number_editor_get_property;
170 * ModestNumberEditor:value:
172 * The current value of the number editor.
174 g_object_class_install_property (gobject_class, PROP_VALUE,
175 g_param_spec_int ("value",
177 "The current value of number editor",
180 0, G_PARAM_READWRITE));
182 ModestNumberEditor_signal[RANGE_ERROR] =
183 g_signal_new ("range_error", MODEST_TYPE_NUMBER_EDITOR,
184 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET
185 (ModestNumberEditorClass, range_error),
186 g_signal_accumulator_true_handled, NULL,
187 modest_marshal_BOOLEAN__ENUM,
188 G_TYPE_BOOLEAN, 1, MODEST_TYPE_NUMBER_EDITOR_ERROR_TYPE);
190 ModestNumberEditor_signal[VALID_CHANGED] =
191 g_signal_new ("valid_changed", MODEST_TYPE_NUMBER_EDITOR,
192 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET
193 (ModestNumberEditorClass, valid_changed),
194 g_signal_accumulator_true_handled, NULL,
195 g_cclosure_marshal_VOID__BOOLEAN,
196 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
200 modest_number_editor_finalize (GObject *self)
202 ModestNumberEditorPrivate *priv;
204 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (self);
207 if (priv->select_all_idle_id)
208 g_source_remove (priv->select_all_idle_id);
210 /* Call parent class finalize, if have one */
211 if (G_OBJECT_CLASS (parent_class)->finalize)
212 G_OBJECT_CLASS (parent_class)->finalize(self);
216 modest_number_editor_init (ModestNumberEditor *editor)
218 ModestNumberEditorPrivate *priv;
220 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
223 priv->select_all_idle_id = 0;
225 /* Connect child widget signals */
226 g_signal_connect (GTK_OBJECT (editor), "changed",
227 G_CALLBACK (modest_number_editor_entry_changed),
230 g_signal_connect (GTK_OBJECT (editor), "focus-out-event",
231 G_CALLBACK (modest_number_editor_entry_focusout),
234 hildon_gtk_entry_set_input_mode (GTK_ENTRY (editor),
235 HILDON_GTK_INPUT_MODE_NUMERIC);
237 modest_number_editor_set_range (editor, G_MININT, G_MAXINT);
239 priv->is_valid = TRUE;
242 /* Format given number to editor field, no checks performed, all signals
243 are sent normally. */
245 modest_number_editor_real_set_value (ModestNumberEditor *editor,
248 /* FIXME: That looks REALLY bad */
251 /* Update text in entry to new value */
252 g_snprintf (buffer, sizeof (buffer), "%d", value);
253 gtk_entry_set_text (GTK_ENTRY (editor), buffer);
257 add_select_all_idle (ModestNumberEditor *editor)
259 ModestNumberEditorPrivate *priv;
261 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
263 if (! priv->select_all_idle_id)
265 priv->select_all_idle_id =
266 g_idle_add((GSourceFunc) modest_number_editor_select_all, editor);
271 modest_number_editor_validate_value (ModestNumberEditor *editor,
272 gboolean allow_intermediate)
274 ModestNumberEditorPrivate *priv;
275 gint error_code, fixup_value;
280 gboolean is_valid = TRUE;
282 g_assert (MODEST_IS_NUMBER_EDITOR(editor));
284 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
287 text = gtk_entry_get_text (GTK_ENTRY (editor));
289 fixup_value = priv->default_val;
291 if (text && text[0]) {
292 /* Try to convert entry text to number */
293 value = strtol (text, &tail, 10);
295 /* Check if conversion succeeded */
297 /* Check if value is in allowed range. This is tricky in those
298 cases when user is editing a value.
299 For example: Range = [100, 500] and user have just inputted "4".
300 This should not lead into error message. Otherwise value is
301 resetted back to "100" and next "4" press will reset it back
304 if (allow_intermediate) {
305 /* We now have the following error cases:
306 * If inputted value as above maximum and
307 maximum is either positive or then maximum
308 negative and value is positive.
309 * If inputted value is below minimum and minimum
310 is negative or minumum positive and value
312 In all other cases situation can be fixed just by
313 adding new numbers to the string.
315 if (value > priv->end && (priv->end >= 0 || (priv->end < 0 && value >= 0))) {
316 error_code = MODEST_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED;
317 fixup_value = priv->end;
319 } else if (value < priv->start && (priv->start < 0 || (priv->start >= 0 && value <= 0))) {
320 error_code = MODEST_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED;
321 fixup_value = priv->start;
325 if (value > priv->end) {
326 error_code = MODEST_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED;
327 fixup_value = priv->end;
329 } else if (value < priv->start) {
330 error_code = MODEST_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED;
331 fixup_value = priv->start;
335 /* The only valid case when conversion can fail is when we
336 have plain '-', intermediate forms are allowed AND
337 minimum bound is negative */
340 if (! allow_intermediate || strcmp (text, "-") != 0 || priv->start >= 0)
341 error_code = MODEST_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE;
345 if (! allow_intermediate)
346 error_code = MODEST_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE;
349 if (error_code != -1) {
350 /* If entry is empty and intermediate forms are nor allowed,
352 /* Change to default value */
353 modest_number_editor_set_value (editor, fixup_value);
354 g_signal_emit (editor, ModestNumberEditor_signal[RANGE_ERROR], 0, error_code, &r);
355 add_select_all_idle (editor);
358 if (priv->is_valid != is_valid) {
359 g_signal_emit (editor, ModestNumberEditor_signal[VALID_CHANGED], 0, is_valid);
360 priv->is_valid = is_valid;
365 modest_number_editor_entry_changed (GtkWidget *widget,
368 g_assert (MODEST_IS_NUMBER_EDITOR (data));
369 modest_number_editor_validate_value (MODEST_NUMBER_EDITOR (data), TRUE);
370 g_object_notify (G_OBJECT (data), "value");
374 modest_number_editor_entry_focusout (GtkWidget *widget,
375 GdkEventFocus *event,
378 g_assert (MODEST_IS_NUMBER_EDITOR(data));
380 modest_number_editor_validate_value (MODEST_NUMBER_EDITOR(data), FALSE);
385 modest_number_editor_range_error (ModestNumberEditor *editor,
386 ModestNumberEditorErrorType type)
390 gchar *err_msg = NULL;
391 ModestNumberEditorPrivate *priv;
393 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
399 /* Construct error message */
402 case MODEST_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED:
403 err_msg = g_strdup_printf (_HL("ckct_ib_maximum_value"), max, max);
406 case MODEST_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED:
407 err_msg = g_strdup_printf (_HL("ckct_ib_minimum_value"), min, min);
410 case MODEST_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE:
412 g_strdup_printf (_HL("ckct_ib_set_a_value_within_range"), min, max);
416 /* Infoprint error */
419 hildon_banner_show_information (GTK_WIDGET (GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET(editor),
420 GTK_TYPE_WINDOW))), NULL, err_msg);
428 * modest_number_editor_new:
429 * @min: minimum accepted value
430 * @max: maximum accepted value
432 * Creates new number editor
434 * Returns: a new #ModestNumberEditor widget
437 modest_number_editor_new (gint min,
440 ModestNumberEditor *editor = g_object_new (MODEST_TYPE_NUMBER_EDITOR, NULL);
442 /* Set user inputted range to editor */
443 modest_number_editor_set_range (editor, min, max);
445 return GTK_WIDGET (editor);
449 * modest_number_editor_set_range:
450 * @editor: a #ModestNumberEditor widget
451 * @min: minimum accepted value
452 * @max: maximum accepted value
454 * Sets accepted number range for editor
457 modest_number_editor_set_range (ModestNumberEditor *editor,
461 ModestNumberEditorPrivate *priv;
462 gchar buffer_min[32], buffer_max[32];
465 g_return_if_fail (MODEST_IS_NUMBER_EDITOR (editor));
467 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
470 /* Set preferences */
471 priv->start = MIN (min, max);
472 priv->end = MAX (min, max);
474 /* Find maximum allowed length of value */
475 g_snprintf (buffer_min, sizeof (buffer_min), "%d", min);
476 g_snprintf (buffer_max, sizeof (buffer_max), "%d", max);
477 a = strlen (buffer_min);
478 b = strlen (buffer_max);
480 /* Set maximum size of entry */
481 gtk_entry_set_width_chars (GTK_ENTRY (editor), MAX (a, b));
482 modest_number_editor_set_value (editor, priv->start);
486 * modest_number_editor_is_valid:
487 * @editor: pointer to #ModestNumberEditor
489 * Returns: if @editor contents are valid
492 modest_number_editor_is_valid (ModestNumberEditor *editor)
494 ModestNumberEditorPrivate *priv;
496 g_return_val_if_fail (MODEST_IS_NUMBER_EDITOR (editor), FALSE);
498 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
501 return priv->is_valid;
506 * modest_number_editor_get_value:
507 * @editor: pointer to #ModestNumberEditor
509 * Returns: current NumberEditor value
512 modest_number_editor_get_value (ModestNumberEditor *editor)
514 ModestNumberEditorPrivate *priv;
516 g_return_val_if_fail (MODEST_IS_NUMBER_EDITOR (editor), 0);
518 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
521 return atoi (gtk_entry_get_text (GTK_ENTRY (editor)));
525 * modest_number_editor_set_value:
526 * @editor: pointer to #ModestNumberEditor
527 * @value: numeric value for number editor
529 * Sets numeric value for number editor
532 modest_number_editor_set_value (ModestNumberEditor *editor,
535 ModestNumberEditorPrivate *priv;
537 g_return_if_fail (MODEST_IS_NUMBER_EDITOR (editor));
539 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
542 g_return_if_fail (value <= priv->end);
543 g_return_if_fail (value >= priv->start);
545 priv->default_val = value;
546 modest_number_editor_real_set_value (editor, value);
547 g_object_notify (G_OBJECT(editor), "value");
550 /* When calling gtk_entry_set_text, the entry widget does things that can
551 * cause the whole widget to redraw. This redrawing is delayed and if any
552 * selections are made right after calling the gtk_entry_set_text the
553 * setting of the selection might seem to have no effect.
555 * If the selection is delayed with a lower priority than the redrawing,
556 * the selection should stick. Calling this function with g_idle_add should
560 modest_number_editor_select_all (ModestNumberEditor *editor)
562 ModestNumberEditorPrivate *priv;
564 g_return_val_if_fail (MODEST_IS_NUMBER_EDITOR (editor), FALSE);
566 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
569 GDK_THREADS_ENTER ();
570 gtk_editable_select_region (GTK_EDITABLE (editor), 0, -1);
571 priv->select_all_idle_id = 0;
572 GDK_THREADS_LEAVE ();
577 modest_number_editor_set_property (GObject *object,
582 ModestNumberEditor *editor;
584 editor = MODEST_NUMBER_EDITOR (object);
589 modest_number_editor_set_value (editor, g_value_get_int (value));
593 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
599 modest_number_editor_get_property (GObject *object,
604 ModestNumberEditor *editor;
606 editor = MODEST_NUMBER_EDITOR (object);
611 g_value_set_int(value, modest_number_editor_get_value (editor));
615 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
621 /* enumerations from "modest-number-editor.h" */
623 modest_number_editor_error_type_get_type (void)
625 static GType etype = 0;
627 static const GEnumValue values[] = {
628 { MODEST_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED, "MODEST_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED", "maximum-value-exceed" },
629 { MODEST_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED, "MODEST_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED", "minimum-value-exceed" },
630 { MODEST_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE, "MODEST_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE", "erroneous-value" },
633 etype = g_enum_register_static ("ModestNumberEditorErrorType", values);