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>
41 #define _(String) dgettext("modest-libs", String)
43 typedef struct _ModestNumberEditorPrivate ModestNumberEditorPrivate;
45 #define MODEST_NUMBER_EDITOR_GET_PRIVATE(obj) \
46 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MODEST_TYPE_NUMBER_EDITOR, \
47 ModestNumberEditorPrivate));
49 struct _ModestNumberEditorPrivate
51 gint start; /* Minimum */
52 gint end; /* Maximum */
56 guint select_all_idle_id; /* Selection repaint hack
57 see modest_number_editor_select_all */
62 modest_number_editor_class_init (ModestNumberEditorClass *editor_class);
65 modest_number_editor_init (ModestNumberEditor *editor);
68 modest_number_editor_entry_focusout (GtkWidget *widget,
73 modest_number_editor_entry_changed (GtkWidget *widget,
77 modest_number_editor_finalize (GObject *self);
80 modest_number_editor_range_error (ModestNumberEditor *editor,
81 ModestNumberEditorErrorType type);
84 modest_number_editor_select_all (ModestNumberEditor *editor);
87 modest_number_editor_validate_value (ModestNumberEditor *editor,
88 gboolean allow_intermediate);
91 modest_number_editor_set_property (GObject * object,
97 modest_number_editor_get_property (GObject *object,
113 static GtkContainerClass* parent_class;
115 static guint ModestNumberEditor_signal[LAST_SIGNAL] = {0};
118 * modest_number_editor_get_type:
120 * Returns GType for ModestNumberEditor.
122 * Returns: ModestNumberEditor type
125 modest_number_editor_get_type (void)
127 static GType editor_type = 0;
131 static const GTypeInfo editor_info =
133 sizeof (ModestNumberEditorClass),
134 NULL, /* base_init */
135 NULL, /* base_finalize */
136 (GClassInitFunc) modest_number_editor_class_init,
137 NULL, /* class_finalize */
138 NULL, /* class_data */
139 sizeof (ModestNumberEditor),
141 (GInstanceInitFunc) modest_number_editor_init,
143 editor_type = g_type_register_static (HILDON_TYPE_ENTRY,
144 "ModestNumberEditor",
151 modest_number_editor_class_init (ModestNumberEditorClass *editor_class)
153 GObjectClass *gobject_class = G_OBJECT_CLASS (editor_class);
155 g_type_class_add_private (editor_class,
156 sizeof (ModestNumberEditorPrivate));
158 parent_class = g_type_class_peek_parent (editor_class);
160 editor_class->range_error = modest_number_editor_range_error;
162 gobject_class->finalize = modest_number_editor_finalize;
163 gobject_class->set_property = modest_number_editor_set_property;
164 gobject_class->get_property = modest_number_editor_get_property;
167 * ModestNumberEditor:value:
169 * The current value of the number editor.
171 g_object_class_install_property (gobject_class, PROP_VALUE,
172 g_param_spec_int ("value",
174 "The current value of number editor",
177 0, G_PARAM_READWRITE));
179 ModestNumberEditor_signal[RANGE_ERROR] =
180 g_signal_new ("range_error", MODEST_TYPE_NUMBER_EDITOR,
181 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET
182 (ModestNumberEditorClass, range_error),
183 g_signal_accumulator_true_handled, NULL,
184 modest_marshal_BOOLEAN__ENUM,
185 G_TYPE_BOOLEAN, 1, MODEST_TYPE_NUMBER_EDITOR_ERROR_TYPE);
189 modest_number_editor_finalize (GObject *self)
191 ModestNumberEditorPrivate *priv;
193 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (self);
196 if (priv->select_all_idle_id)
197 g_source_remove (priv->select_all_idle_id);
199 /* Call parent class finalize, if have one */
200 if (G_OBJECT_CLASS (parent_class)->finalize)
201 G_OBJECT_CLASS (parent_class)->finalize(self);
205 modest_number_editor_init (ModestNumberEditor *editor)
207 ModestNumberEditorPrivate *priv;
209 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
212 priv->select_all_idle_id = 0;
214 /* Connect child widget signals */
215 g_signal_connect (GTK_OBJECT (editor), "changed",
216 G_CALLBACK (modest_number_editor_entry_changed),
219 g_signal_connect (GTK_OBJECT (editor), "focus-out-event",
220 G_CALLBACK (modest_number_editor_entry_focusout),
224 g_object_set (G_OBJECT (editor),
225 "hildon-input-mode", HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
227 modest_number_editor_set_range (editor, G_MININT, G_MAXINT);
230 /* Format given number to editor field, no checks performed, all signals
231 are sent normally. */
233 modest_number_editor_real_set_value (ModestNumberEditor *editor,
236 /* FIXME: That looks REALLY bad */
239 /* Update text in entry to new value */
240 g_snprintf (buffer, sizeof (buffer), "%d", value);
241 gtk_entry_set_text (GTK_ENTRY (editor), buffer);
245 add_select_all_idle (ModestNumberEditor *editor)
247 ModestNumberEditorPrivate *priv;
249 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
251 if (! priv->select_all_idle_id)
253 priv->select_all_idle_id =
254 g_idle_add((GSourceFunc) modest_number_editor_select_all, editor);
259 modest_number_editor_validate_value (ModestNumberEditor *editor,
260 gboolean allow_intermediate)
262 ModestNumberEditorPrivate *priv;
263 gint error_code, fixup_value;
269 g_assert (MODEST_IS_NUMBER_EDITOR(editor));
271 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
274 text = gtk_entry_get_text (GTK_ENTRY (editor));
276 fixup_value = priv->default_val;
280 /* Try to convert entry text to number */
281 value = strtol (text, &tail, 10);
283 /* Check if conversion succeeded */
286 /* Check if value is in allowed range. This is tricky in those
287 cases when user is editing a value.
288 For example: Range = [100, 500] and user have just inputted "4".
289 This should not lead into error message. Otherwise value is
290 resetted back to "100" and next "4" press will reset it back
292 if (allow_intermediate)
294 /* We now have the following error cases:
295 * If inputted value as above maximum and
296 maximum is either positive or then maximum
297 negative and value is positive.
298 * If inputted value is below minimum and minimum
299 is negative or minumum positive and value
301 In all other cases situation can be fixed just by
302 adding new numbers to the string.
304 if (value > priv->end && (priv->end >= 0 || (priv->end < 0 && value >= 0)))
306 error_code = MODEST_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED;
307 fixup_value = priv->end;
309 else if (value < priv->start && (priv->start < 0 || (priv->start >= 0 && value <= 0)))
311 error_code = MODEST_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED;
312 fixup_value = priv->start;
317 if (value > priv->end) {
318 error_code = MODEST_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED;
319 fixup_value = priv->end;
321 else if (value < priv->start) {
322 error_code = MODEST_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED;
323 fixup_value = priv->start;
327 /* The only valid case when conversion can fail is when we
328 have plain '-', intermediate forms are allowed AND
329 minimum bound is negative */
330 else if (! allow_intermediate || strcmp (text, "-") != 0 || priv->start >= 0)
331 error_code = MODEST_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE;
333 else if (! allow_intermediate)
334 error_code = MODEST_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE;
336 if (error_code != -1)
338 /* If entry is empty and intermediate forms are nor allowed,
340 /* Change to default value */
341 modest_number_editor_set_value (editor, fixup_value);
342 g_signal_emit (editor, ModestNumberEditor_signal[RANGE_ERROR], 0, error_code, &r);
343 add_select_all_idle (editor);
348 modest_number_editor_entry_changed (GtkWidget *widget,
351 g_assert (MODEST_IS_NUMBER_EDITOR (data));
352 modest_number_editor_validate_value (MODEST_NUMBER_EDITOR (data), TRUE);
353 g_object_notify (G_OBJECT (data), "value");
357 modest_number_editor_entry_focusout (GtkWidget *widget,
358 GdkEventFocus *event,
361 g_assert (MODEST_IS_NUMBER_EDITOR(data));
363 modest_number_editor_validate_value (MODEST_NUMBER_EDITOR(data), FALSE);
368 modest_number_editor_range_error (ModestNumberEditor *editor,
369 ModestNumberEditorErrorType type)
373 gchar *err_msg = NULL;
374 ModestNumberEditorPrivate *priv;
376 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
382 /* Construct error message */
385 case MODEST_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED:
386 err_msg = g_strdup_printf (_("ckct_ib_maximum_value"), max, max);
389 case MODEST_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED:
390 err_msg = g_strdup_printf (_("ckct_ib_minimum_value"), min, min);
393 case MODEST_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE:
395 g_strdup_printf (_("ckct_ib_set_a_value_within_range"), min, max);
399 /* Infoprint error */
402 hildon_banner_show_information (GTK_WIDGET (GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET(editor),
403 GTK_TYPE_WINDOW))), NULL, err_msg);
411 * modest_number_editor_new:
412 * @min: minimum accepted value
413 * @max: maximum accepted value
415 * Creates new number editor
417 * Returns: a new #ModestNumberEditor widget
420 modest_number_editor_new (gint min,
423 ModestNumberEditor *editor = g_object_new (MODEST_TYPE_NUMBER_EDITOR, NULL);
425 /* Set user inputted range to editor */
426 modest_number_editor_set_range (editor, min, max);
428 return GTK_WIDGET (editor);
432 * modest_number_editor_set_range:
433 * @editor: a #ModestNumberEditor widget
434 * @min: minimum accepted value
435 * @max: maximum accepted value
437 * Sets accepted number range for editor
440 modest_number_editor_set_range (ModestNumberEditor *editor,
444 ModestNumberEditorPrivate *priv;
445 gchar buffer_min[32], buffer_max[32];
448 g_return_if_fail (MODEST_IS_NUMBER_EDITOR (editor));
450 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
453 /* Set preferences */
454 priv->start = MIN (min, max);
455 priv->end = MAX (min, max);
457 /* Find maximum allowed length of value */
458 g_snprintf (buffer_min, sizeof (buffer_min), "%d", min);
459 g_snprintf (buffer_max, sizeof (buffer_max), "%d", max);
460 a = strlen (buffer_min);
461 b = strlen (buffer_max);
463 /* Set maximum size of entry */
464 gtk_entry_set_width_chars (GTK_ENTRY (editor), MAX (a, b));
465 modest_number_editor_set_value (editor, priv->start);
469 * modest_number_editor_get_value:
470 * @editor: pointer to #ModestNumberEditor
472 * Returns: current NumberEditor value
475 modest_number_editor_get_value (ModestNumberEditor *editor)
477 ModestNumberEditorPrivate *priv;
479 g_return_val_if_fail (MODEST_IS_NUMBER_EDITOR (editor), 0);
481 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
484 return atoi (gtk_entry_get_text (GTK_ENTRY (editor)));
488 * modest_number_editor_set_value:
489 * @editor: pointer to #ModestNumberEditor
490 * @value: numeric value for number editor
492 * Sets numeric value for number editor
495 modest_number_editor_set_value (ModestNumberEditor *editor,
498 ModestNumberEditorPrivate *priv;
500 g_return_if_fail (MODEST_IS_NUMBER_EDITOR (editor));
502 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
505 g_return_if_fail (value <= priv->end);
506 g_return_if_fail (value >= priv->start);
508 priv->default_val = value;
509 modest_number_editor_real_set_value (editor, value);
510 g_object_notify (G_OBJECT(editor), "value");
513 /* When calling gtk_entry_set_text, the entry widget does things that can
514 * cause the whole widget to redraw. This redrawing is delayed and if any
515 * selections are made right after calling the gtk_entry_set_text the
516 * setting of the selection might seem to have no effect.
518 * If the selection is delayed with a lower priority than the redrawing,
519 * the selection should stick. Calling this function with g_idle_add should
523 modest_number_editor_select_all (ModestNumberEditor *editor)
525 ModestNumberEditorPrivate *priv;
527 g_return_val_if_fail (MODEST_IS_NUMBER_EDITOR (editor), FALSE);
529 priv = MODEST_NUMBER_EDITOR_GET_PRIVATE (editor);
532 GDK_THREADS_ENTER ();
533 gtk_editable_select_region (GTK_EDITABLE (editor), 0, -1);
534 priv->select_all_idle_id = 0;
535 GDK_THREADS_LEAVE ();
540 modest_number_editor_set_property (GObject *object,
545 ModestNumberEditor *editor;
547 editor = MODEST_NUMBER_EDITOR (object);
552 modest_number_editor_set_value (editor, g_value_get_int (value));
556 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
562 modest_number_editor_get_property (GObject *object,
567 ModestNumberEditor *editor;
569 editor = MODEST_NUMBER_EDITOR (object);
574 g_value_set_int(value, modest_number_editor_get_value (editor));
578 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
584 /* enumerations from "modest-number-editor.h" */
586 modest_number_editor_error_type_get_type (void)
588 static GType etype = 0;
590 static const GEnumValue values[] = {
591 { MODEST_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED, "MODEST_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED", "maximum-value-exceed" },
592 { MODEST_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED, "MODEST_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED", "minimum-value-exceed" },
593 { MODEST_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE, "MODEST_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE", "erroneous-value" },
596 etype = g_enum_register_static ("ModestNumberEditorErrorType", values);