161eccc4f98606a7cef22da83e89984e916a2451
[hildon] / src / hildon-number-editor.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 /**
26  * SECTION:hildon-number-editor
27  * @short_description: A widget used to enter a number within a pre-defined range.
28  *
29  * HildonNumberEditor is used to enter a number from a specific range. 
30  * There are two buttons to scroll the value in number field. 
31  * Manual input is also possible.
32  *
33  * <example>
34  * <title>HildonNumberEditor example</title>
35  * <programlisting>
36  * number_editor = hildon_number_editor_new (-250, 500);
37  * hildon_number_editor_set_range (number_editor, 0, 100);
38  * </programlisting>
39  * </example>
40  */
41
42 #ifdef                                          HAVE_CONFIG_H
43 #include                                        <config.h>
44 #endif
45
46 #include                                        "hildon-number-editor.h"
47 #include                                        <gdk/gdkkeysyms.h>
48 #include                                        <gtk/gtk.h>
49 #include                                        <string.h>
50 #include                                        <stdio.h>
51 #include                                        <stdlib.h>
52 #include                                        "hildon-marshalers.h"
53 #include                                        "hildon-private.h"
54 #include                                        "hildon-defines.h"
55 #include                                        "hildon-enum-types.h"
56 #include                                        "hildon-banner.h"
57 #include                                        <libintl.h>
58 #include                                        "hildon-number-editor-private.h"
59
60 #define                                         _(String) dgettext("hildon-libs", String)
61
62 /*Pixel spec defines*/
63 #define                                         NUMBER_EDITOR_HEIGHT 30
64
65 /* Size of plus and minus buttons */
66 #define                                         BUTTON_HEIGHT 30
67
68 #define                                         BUTTON_WIDTH 30
69
70 static void
71 hildon_number_editor_class_init                 (HildonNumberEditorClass *editor_class);
72
73 static void
74 hildon_number_editor_init                       (HildonNumberEditor *editor);
75
76 static gboolean
77 hildon_number_editor_entry_focusout             (GtkWidget *widget, 
78                                                  GdkEventFocus *event,
79                                                  gpointer data);
80
81 static void
82 hildon_number_editor_entry_changed              (GtkWidget *widget, 
83                                                  gpointer data);
84
85 static void
86 hildon_number_editor_size_request               (GtkWidget *widget,
87                                                  GtkRequisition *requisition);
88
89 static void
90 set_widget_allocation                           (GtkWidget *widget, 
91                                                  GtkAllocation *alloc,
92                                                  const GtkAllocation *allocation);
93
94 static void
95 hildon_number_editor_size_allocate              (GtkWidget *widget,
96                                                  GtkAllocation *allocation);
97
98 static gboolean
99 hildon_number_editor_focus                      (GtkWidget *widget,
100                                                  GtkDirectionType direction);
101
102 static gboolean
103 hildon_number_editor_entry_keypress             (GtkWidget *widget, 
104                                                  GdkEventKey *event,
105                                                  gpointer data);
106
107 static gboolean
108 hildon_number_editor_button_pressed             (GtkWidget *widget, 
109                                                  GdkEventButton *event,
110                                                  gpointer data);
111
112 static gboolean
113 hildon_number_editor_entry_button_released      (GtkWidget *widget,
114                                                  GdkEventButton *event,
115                                                  gpointer data);
116 static gboolean
117 hildon_number_editor_button_released            (GtkWidget *widget,
118                                                  GdkEvent *event,
119                                                  HildonNumberEditor *editor);
120 static gboolean
121 do_mouse_timeout                                (HildonNumberEditor *editor);
122
123 static void
124 change_numbers                                  (HildonNumberEditor *editor, 
125                                                  gint update);
126
127 static void
128 hildon_number_editor_forall                     (GtkContainer *container, 
129                                                  gboolean include_internals,
130                                                  GtkCallback callback, 
131                                                  gpointer callback_data);
132
133 static void
134 hildon_number_editor_destroy                    (GtkObject *self);
135
136 static gboolean
137 hildon_number_editor_start_timer                (HildonNumberEditor *editor);
138
139 static void
140 hildon_number_editor_finalize                   (GObject *self);
141
142 static gboolean
143 hildon_number_editor_range_error                (HildonNumberEditor *editor,
144                                                  HildonNumberEditorErrorType type);
145
146 static gboolean
147 hildon_number_editor_select_all                 (HildonNumberEditorPrivate *priv);
148
149 static void
150 hildon_number_editor_validate_value             (HildonNumberEditor *editor, 
151                                                  gboolean allow_intermediate);
152     
153 static void 
154 hildon_number_editor_set_property               (GObject * object,
155                                                  guint prop_id,
156                                                  const GValue * value,
157                                                  GParamSpec * pspec);
158
159 static void
160 hildon_number_editor_get_property               (GObject *object,
161                                                  guint prop_id,
162                                                  GValue *value, 
163                                                  GParamSpec * pspec);
164
165 enum
166 {
167     RANGE_ERROR,
168     LAST_SIGNAL
169 };
170
171 enum {
172     PROP_0,
173     PROP_VALUE
174 };
175
176 static GtkContainerClass*                       parent_class;
177
178 static guint                                    HildonNumberEditor_signal[LAST_SIGNAL] = {0};
179
180 /**
181  * hildon_number_editor_get_type:
182  *
183  * Returns GType for HildonNumberEditor.
184  *
185  * Returns: HildonNumberEditor type
186  */
187 GType G_GNUC_CONST
188 hildon_number_editor_get_type                   (void)
189 {
190     static GType editor_type = 0;
191
192     if (!editor_type)
193     {
194         static const GTypeInfo editor_info =
195         {
196             sizeof (HildonNumberEditorClass),
197             NULL,       /* base_init */
198             NULL,       /* base_finalize */
199             (GClassInitFunc) hildon_number_editor_class_init,
200             NULL,       /* class_finalize */
201             NULL,       /* class_data */
202             sizeof (HildonNumberEditor),
203             0,  /* n_preallocs */
204             (GInstanceInitFunc) hildon_number_editor_init,
205         };
206         editor_type = g_type_register_static (GTK_TYPE_CONTAINER,
207                 "HildonNumberEditor",
208                 &editor_info, 0);
209     }
210     return editor_type;
211 }
212
213 static void
214 hildon_number_editor_class_init                 (HildonNumberEditorClass *editor_class)
215 {
216     GtkContainerClass *container_class = GTK_CONTAINER_CLASS (editor_class);
217     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (editor_class);
218     GObjectClass *gobject_class = G_OBJECT_CLASS (editor_class);
219
220     g_type_class_add_private (editor_class,
221             sizeof (HildonNumberEditorPrivate));
222
223     parent_class = g_type_class_peek_parent (editor_class);
224
225     widget_class->size_request              = hildon_number_editor_size_request;
226     widget_class->size_allocate             = hildon_number_editor_size_allocate;
227     widget_class->focus                     = hildon_number_editor_focus;
228
229     editor_class->range_error = hildon_number_editor_range_error;
230
231     /* Because we derived our widget from GtkContainer, we should override 
232        forall method */
233     container_class->forall                 = hildon_number_editor_forall;
234     GTK_OBJECT_CLASS(editor_class)->destroy = hildon_number_editor_destroy;
235     gobject_class->finalize                 = hildon_number_editor_finalize;
236     gobject_class->set_property             = hildon_number_editor_set_property;
237     gobject_class->get_property             = hildon_number_editor_get_property;
238
239     /**
240      * HildonNumberEditor:value:
241      *
242      * The current value of the number editor.
243      */
244     g_object_class_install_property (gobject_class, PROP_VALUE,
245             g_param_spec_int ("value",
246                 "Value",
247                 "The current value of number editor",
248                 G_MININT,
249                 G_MAXINT,
250                 0, G_PARAM_READWRITE));
251
252     HildonNumberEditor_signal[RANGE_ERROR] =
253         g_signal_new ("range_error", HILDON_TYPE_NUMBER_EDITOR,
254                 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET
255                 (HildonNumberEditorClass, range_error),
256                 g_signal_accumulator_true_handled, NULL,
257                 _hildon_marshal_BOOLEAN__ENUM,
258                 G_TYPE_BOOLEAN, 1, HILDON_TYPE_NUMBER_EDITOR_ERROR_TYPE);
259 }
260
261 static void
262 hildon_number_editor_forall                     (GtkContainer *container, 
263                                                  gboolean include_internals,
264                                                  GtkCallback callback, 
265                                                  gpointer callback_data)
266 {
267     HildonNumberEditorPrivate *priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (container);
268
269     g_assert (callback != NULL);
270     g_assert (priv);
271
272     if (! include_internals)
273         return;
274
275     /* Enumerate child widgets */
276     (*callback) (priv->minus, callback_data);
277     (*callback) (priv->num_entry, callback_data);
278     (*callback) (priv->plus, callback_data);
279 }
280
281 static void
282 hildon_number_editor_destroy                    (GtkObject *self)
283 {
284     HildonNumberEditorPrivate *priv;
285
286     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (self);
287     g_assert (priv);
288
289     /* Free child widgets */
290     if (priv->minus)
291     {
292         gtk_widget_unparent (priv->minus);
293         priv->minus = NULL;
294     }
295     if (priv->num_entry)
296     {
297         gtk_widget_unparent (priv->num_entry);
298         priv->num_entry = NULL;
299     }
300     if (priv->plus)
301     {
302         gtk_widget_unparent (priv->plus);
303         priv->plus = NULL;
304     }
305
306     if (GTK_OBJECT_CLASS (parent_class)->destroy)
307         GTK_OBJECT_CLASS (parent_class)->destroy(self);
308 }
309
310 static void
311 hildon_number_editor_stop_repeat_timer          (HildonNumberEditorPrivate *priv)
312 {
313     g_assert (priv != NULL);
314
315     if (priv->button_event_id)
316     {
317         g_source_remove (priv->button_event_id);
318         priv->button_event_id = 0;
319     }
320 }
321
322 static void
323 hildon_number_editor_finalize                   (GObject *self)
324 {
325     HildonNumberEditorPrivate *priv;
326
327     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (self);
328     g_assert (priv);
329
330     /* Free timers */
331     hildon_number_editor_stop_repeat_timer (priv);
332
333     if (priv->select_all_idle_id)
334         g_source_remove (priv->select_all_idle_id);
335
336     /* Call parent class finalize, if have one */
337     if (G_OBJECT_CLASS (parent_class)->finalize)
338         G_OBJECT_CLASS (parent_class)->finalize(self);
339 }
340
341 static void
342 hildon_number_editor_init                       (HildonNumberEditor *editor)
343 {
344     HildonNumberEditorPrivate *priv;
345
346     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
347     g_assert (priv);
348
349     GTK_WIDGET_SET_FLAGS (GTK_WIDGET (editor), GTK_NO_WINDOW);
350
351     /* Create child widgets */
352     priv->num_entry = gtk_entry_new ();
353     priv->minus = gtk_button_new ();
354     priv->plus = gtk_button_new ();
355
356     gtk_widget_set_name (priv->minus, "ne-minus-button");
357     gtk_widget_set_name (priv->plus, "ne-plus-button" );
358     gtk_widget_set_size_request (priv->minus, BUTTON_WIDTH, BUTTON_HEIGHT);
359     gtk_widget_set_size_request (priv->plus, BUTTON_WIDTH, BUTTON_HEIGHT);
360     gtk_entry_set_alignment (GTK_ENTRY(priv->num_entry), 1);
361
362     GTK_WIDGET_UNSET_FLAGS (priv->minus, GTK_CAN_FOCUS);
363     GTK_WIDGET_UNSET_FLAGS (priv->plus, GTK_CAN_FOCUS);
364
365     priv->button_event_id = 0;
366     priv->select_all_idle_id = 0;
367
368     gtk_widget_set_parent (priv->minus, GTK_WIDGET (editor));
369     gtk_widget_set_parent (priv->num_entry, GTK_WIDGET (editor));
370     gtk_widget_set_parent (priv->plus, GTK_WIDGET (editor));
371
372     /* Connect child widget signals */
373     g_signal_connect (GTK_OBJECT (priv->num_entry), "changed",
374             G_CALLBACK (hildon_number_editor_entry_changed),
375             editor);
376
377     g_signal_connect (GTK_OBJECT (priv->num_entry), "focus-out-event",
378             G_CALLBACK (hildon_number_editor_entry_focusout),
379             editor);
380
381     g_signal_connect (GTK_OBJECT (priv->num_entry), "key-press-event",
382             G_CALLBACK (hildon_number_editor_entry_keypress),
383             editor);
384
385     g_signal_connect (GTK_OBJECT (priv->num_entry), "button-release-event",
386             G_CALLBACK (hildon_number_editor_entry_button_released),
387             NULL);
388
389     g_signal_connect (GTK_OBJECT (priv->minus), "button-press-event",
390             G_CALLBACK (hildon_number_editor_button_pressed),
391             editor);
392
393     g_signal_connect (GTK_OBJECT (priv->plus), "button-press-event",
394             G_CALLBACK (hildon_number_editor_button_pressed),
395             editor);
396
397     g_signal_connect (GTK_OBJECT (priv->minus), "button-release-event",
398             G_CALLBACK (hildon_number_editor_button_released),
399             editor);
400
401     g_signal_connect (GTK_OBJECT (priv->plus), "button-release-event",
402             G_CALLBACK (hildon_number_editor_button_released),
403             editor);
404
405     g_signal_connect (GTK_OBJECT (priv->minus), "leave-notify-event",
406             G_CALLBACK(hildon_number_editor_button_released),
407             editor);
408
409     g_signal_connect (GTK_OBJECT (priv->plus), "leave-notify-event",
410             G_CALLBACK (hildon_number_editor_button_released),
411             editor);
412
413 #ifdef MAEMO_GTK 
414     g_object_set (G_OBJECT (priv->num_entry),
415             "hildon-input-mode", HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
416 #endif
417
418     gtk_widget_show (priv->num_entry);
419     gtk_widget_show (priv->minus);
420     gtk_widget_show (priv->plus);
421
422     hildon_number_editor_set_range (editor, G_MININT, G_MAXINT);
423 }
424
425 static gboolean
426 hildon_number_editor_entry_button_released      (GtkWidget *widget,
427                                                  GdkEventButton *event,
428                                                  gpointer data)
429 {
430     gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
431     return FALSE;
432 }
433
434 static gboolean
435 hildon_number_editor_button_released            (GtkWidget *widget, 
436                                                  GdkEvent *event,
437                                                  HildonNumberEditor *editor)
438 {
439     HildonNumberEditorPrivate *priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);  
440     g_assert (priv);
441
442     hildon_number_editor_stop_repeat_timer (priv);
443     return FALSE;
444 }
445
446 /* Format given number to editor field, no checks performed, all signals
447    are sent normally. */
448 static void
449 hildon_number_editor_real_set_value             (HildonNumberEditorPrivate *priv, 
450                                                  gint value)
451 {
452     /* FIXME: That looks REALLY bad */
453     gchar buffer[32];
454
455     /* Update text in entry to new value */
456     g_snprintf (buffer, sizeof (buffer), "%d", value);
457     gtk_entry_set_text (GTK_ENTRY (priv->num_entry), buffer);
458 }
459
460 static gboolean
461 hildon_number_editor_button_pressed             (GtkWidget *widget, 
462                                                  GdkEventButton *event,
463                                                  gpointer data)
464 {
465     /* FIXME: XXX Why aren't we using hildon_number_editor_start_timer here? XXX */
466     /* Need to fetch current value from entry and increment or decrement
467        it */
468
469     HildonNumberEditor *editor;
470     HildonNumberEditorPrivate *priv;
471     GtkSettings *settings;
472     guint timeout;
473
474     g_assert (HILDON_IS_NUMBER_EDITOR (data));
475
476     editor = HILDON_NUMBER_EDITOR (data);
477     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
478     g_assert (priv);
479
480     settings = gtk_settings_get_default ();
481     g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
482
483     /* Save type of button pressed */
484     if (GTK_BUTTON (widget) == GTK_BUTTON (priv->plus))
485         priv->button_type = 1;
486     else
487         priv->button_type = -1;
488
489     /* Start repetition timer */
490     if (! priv->button_event_id)
491     {
492         change_numbers (editor, priv->button_type);
493         priv->button_event_id = g_timeout_add (timeout,
494                 (GSourceFunc) hildon_number_editor_start_timer,
495                 editor);
496     }
497
498     return FALSE;
499 }
500
501 static gboolean
502 hildon_number_editor_start_timer                (HildonNumberEditor *editor)
503 {
504     HildonNumberEditorPrivate *priv;
505     GtkSettings *settings;
506     guint timeout;
507
508     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
509     settings = gtk_settings_get_default ();
510     g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
511     timeout *= 8;
512
513     priv->button_event_id = g_timeout_add (timeout,
514             (GSourceFunc) do_mouse_timeout,
515             editor);
516
517     return FALSE;
518 }
519
520 static gboolean
521 do_mouse_timeout                                (HildonNumberEditor *editor)
522 {
523     HildonNumberEditorPrivate *priv;
524     g_assert (HILDON_IS_NUMBER_EDITOR (editor));
525     
526     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
527     g_assert (priv);
528
529     GDK_THREADS_ENTER ();
530
531     /* Update value based on button held */
532     change_numbers (editor, priv->button_type);
533
534     GDK_THREADS_LEAVE ();
535
536     return TRUE;
537 }
538
539 /* Changes the current number value by the amount of update
540    and verifies the result. */
541 static void
542 change_numbers                                  (HildonNumberEditor *editor, 
543                                                  gint update)
544 {
545     HildonNumberEditorPrivate *priv;
546     gint current_value;
547
548     g_assert (HILDON_IS_NUMBER_EDITOR (editor));
549
550     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
551     g_assert (priv);
552
553     current_value = hildon_number_editor_get_value (editor);
554
555     /* We need to rerun validation by hand, since validation
556        done in "changed" callback allows intermediate values */
557     hildon_number_editor_real_set_value (priv, current_value + update);
558     hildon_number_editor_validate_value (editor, FALSE);
559     g_object_notify (G_OBJECT (editor), "value");
560 }
561
562 static void
563 add_select_all_idle                             (HildonNumberEditorPrivate *priv)
564 {
565     g_assert (priv);
566
567     if (! priv->select_all_idle_id)
568     {
569         priv->select_all_idle_id =
570             g_idle_add((GSourceFunc) hildon_number_editor_select_all, priv);
571     }    
572 }
573
574 static void
575 hildon_number_editor_validate_value             (HildonNumberEditor *editor, 
576                                                  gboolean allow_intermediate)
577 {
578     HildonNumberEditorPrivate *priv;
579     gint error_code, fixup_value;
580     const gchar *text;
581     long value;
582     gchar *tail;
583     gboolean r;
584
585     g_assert (HILDON_IS_NUMBER_EDITOR(editor));
586
587     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
588     g_assert (priv);
589
590     text = gtk_entry_get_text (GTK_ENTRY (priv->num_entry));
591     error_code = -1;
592     fixup_value = priv->default_val;
593
594     if (text && text[0])
595     { 
596         /* Try to convert entry text to number */
597         value = strtol (text, &tail, 10);
598
599         /* Check if conversion succeeded */
600         if (tail[0] == 0)
601         {    
602             /* Check if value is in allowed range. This is tricky in those
603                cases when user is editing a value. 
604                For example: Range = [100, 500] and user have just inputted "4".
605                This should not lead into error message. Otherwise value is
606                resetted back to "100" and next "4" press will reset it back
607                and so on. */
608             if (allow_intermediate)
609             {
610                 /* We now have the following error cases:
611                  * If inputted value as above maximum and
612                  maximum is either positive or then maximum
613                  negative and value is positive.
614                  * If inputted value is below minimum and minimum
615                  is negative or minumum positive and value
616                  negative or zero.
617                  In all other cases situation can be fixed just by
618                  adding new numbers to the string.
619                  */
620                 if (value > priv->end && (priv->end >= 0 || (priv->end < 0 && value >= 0)))
621                 {
622                     error_code = HILDON_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED;
623                     fixup_value = priv->end;
624                 }
625                 else if (value < priv->start && (priv->start < 0 || (priv->start >= 0 && value <= 0)))
626                 {
627                     error_code = HILDON_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED;
628                     fixup_value = priv->start;
629                 }
630             }
631             else
632             {
633                 if (value > priv->end) {
634                     error_code = HILDON_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED;
635                     fixup_value = priv->end;
636                 }
637                 else if (value < priv->start) {
638                     error_code = HILDON_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED;
639                     fixup_value = priv->start;
640                 }
641             }
642         }
643         /* The only valid case when conversion can fail is when we
644            have plain '-', intermediate forms are allowed AND
645            minimum bound is negative */
646         else if (! allow_intermediate || strcmp (text, "-") != 0 || priv->start >= 0)
647             error_code = HILDON_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE;
648     }
649     else if (! allow_intermediate)
650         error_code = HILDON_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE;
651
652     if (error_code != -1)
653     {
654         /* If entry is empty and intermediate forms are nor allowed, 
655            emit error signal */
656         /* Change to default value */
657         hildon_number_editor_set_value (editor, fixup_value);
658         g_signal_emit (editor, HildonNumberEditor_signal[RANGE_ERROR], 0, error_code, &r);
659         add_select_all_idle (priv);
660     }
661 }
662
663 static void 
664 hildon_number_editor_entry_changed              (GtkWidget *widget, 
665                                                  gpointer data)
666 {
667     g_assert (HILDON_IS_NUMBER_EDITOR (data));
668     hildon_number_editor_validate_value (HILDON_NUMBER_EDITOR (data), TRUE);
669     g_object_notify (G_OBJECT (data), "value");
670 }
671
672 static void
673 hildon_number_editor_size_request               (GtkWidget *widget,
674                                                  GtkRequisition *requisition)
675 {
676     HildonNumberEditor *editor;
677     HildonNumberEditorPrivate *priv;
678     GtkRequisition req;
679
680     editor = HILDON_NUMBER_EDITOR (widget);
681     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
682     g_assert (priv);
683
684     /* Requested size is size of all child widgets plus border space */
685     gtk_widget_size_request (priv->minus, &req);
686     requisition->width = req.width;
687
688     gtk_widget_size_request (priv->num_entry, &req);
689     requisition->width += req.width;
690
691     gtk_widget_size_request (priv->plus, &req);
692     requisition->width += req.width;
693
694     requisition->width += HILDON_MARGIN_DEFAULT * 2;
695
696     /* FIXME: XXX Height is fixed */
697     requisition->height = NUMBER_EDITOR_HEIGHT;
698 }
699
700 /* Update alloc->width so widget fits, update alloc->x to point to free space */
701 static void
702 set_widget_allocation                           (GtkWidget *widget, 
703                                                  GtkAllocation *alloc,
704                                                  const GtkAllocation *allocation)
705 {
706     GtkRequisition child_requisition;
707
708     gtk_widget_get_child_requisition (widget, &child_requisition);
709
710     /* Fit to widget width */
711     if (allocation->width + allocation->x > alloc->x + child_requisition.width)
712         alloc->width = child_requisition.width;
713     else
714     {
715         alloc->width = allocation->width - (alloc->x - allocation->x);
716         if (alloc->width < 0)
717             alloc->width = 0;
718     }
719
720     gtk_widget_size_allocate (widget, alloc);
721     /* Update x position */
722     alloc->x += alloc->width;
723 }
724
725 static void
726 hildon_number_editor_size_allocate              (GtkWidget *widget,
727                                                  GtkAllocation *allocation)
728 {
729     HildonNumberEditor *editor;
730     HildonNumberEditorPrivate *priv;
731     GtkAllocation alloc;
732
733     editor = HILDON_NUMBER_EDITOR (widget);
734     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
735     g_assert (priv);
736
737     widget->allocation = *allocation;
738
739     /* Add upper border */
740     alloc.y = widget->allocation.y + widget->style->ythickness;
741
742     /* Fix height */
743     if (widget->allocation.height > NUMBER_EDITOR_HEIGHT)
744     {
745         alloc.height = NUMBER_EDITOR_HEIGHT - widget->style->ythickness * 2;
746         alloc.y += (widget->allocation.height - NUMBER_EDITOR_HEIGHT) / 2;
747     }
748     else
749         alloc.height = widget->allocation.height - widget->style->ythickness * 2;
750
751     if (alloc.height < 0)
752         alloc.height = 0;
753
754     /* Add left border */
755     alloc.x = allocation->x + widget->style->xthickness;
756
757     /* Allocate positions for widgets (left-to-right) */
758     set_widget_allocation(priv->minus, &alloc, &widget->allocation);
759     alloc.x += HILDON_MARGIN_DEFAULT;
760
761     set_widget_allocation(priv->num_entry, &alloc, &widget->allocation);
762     alloc.x += HILDON_MARGIN_DEFAULT;
763
764     set_widget_allocation(priv->plus, &alloc, &widget->allocation);
765 }
766
767 static gboolean
768 hildon_number_editor_focus                      (GtkWidget *widget,
769                                                  GtkDirectionType direction)
770 {
771   gboolean retval;
772   GtkDirectionType effective_direction;
773
774   g_assert (HILDON_IS_NUMBER_EDITOR (widget));
775
776   retval = hildon_private_composite_focus (widget, direction, &effective_direction);
777
778   if (retval == TRUE)
779     return GTK_WIDGET_CLASS (parent_class)->focus (widget, effective_direction);
780   else
781     return FALSE;
782 }
783
784 static gboolean
785 hildon_number_editor_entry_focusout             (GtkWidget *widget, 
786                                                  GdkEventFocus *event,
787                                                  gpointer data)
788 {
789     g_assert (HILDON_IS_NUMBER_EDITOR(data));
790
791     hildon_number_editor_validate_value (HILDON_NUMBER_EDITOR(data), FALSE);
792     return FALSE;
793 }
794
795 static gboolean
796 hildon_number_editor_entry_keypress             (GtkWidget *widget, 
797                                                  GdkEventKey *event,
798                                                  gpointer data)
799 {
800     GtkEditable *editable;
801     gint cursor_pos;
802
803     g_assert (HILDON_IS_NUMBER_EDITOR (data));
804
805     editable = GTK_EDITABLE (widget);
806     cursor_pos = gtk_editable_get_position (editable);
807
808     switch (event->keyval)
809     {
810         case GDK_Left:
811             /* If the cursor is on the left, try to decrement */
812             if (cursor_pos == 0) {
813                 change_numbers (HILDON_NUMBER_EDITOR (data), -1);
814                 return TRUE;
815             }
816             break;
817
818         case GDK_Right:
819             /* If the cursor is on the right, try to increment */
820             if (cursor_pos >= g_utf8_strlen(gtk_entry_get_text (GTK_ENTRY (widget)), -1))
821             {
822                 change_numbers (HILDON_NUMBER_EDITOR (data), 1);
823                 gtk_editable_set_position(editable, cursor_pos);
824                 return TRUE;
825             }
826             break;
827
828         default:
829             break;
830     };
831
832     return FALSE;
833 }
834
835 static gboolean
836 hildon_number_editor_range_error                (HildonNumberEditor *editor,
837                                                  HildonNumberEditorErrorType type)
838 {
839
840     gint min, max;
841     gchar *err_msg = NULL;
842     HildonNumberEditorPrivate *priv;
843
844     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
845     g_assert (priv);
846
847     min = priv->start;
848     max = priv->end;
849
850     /* Construct error message */
851     switch (type)
852     {
853         case HILDON_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED:
854             err_msg = g_strdup_printf (_("ckct_ib_maximum_value"), max, max);
855             break;
856
857         case HILDON_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED:
858             err_msg = g_strdup_printf (_("ckct_ib_minimum_value"), min, min);
859             break;
860
861         case HILDON_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE:
862             err_msg =
863                 g_strdup_printf (_("ckct_ib_set_a_value_within_range"), min, max);
864             break;
865     }
866
867     /* Infoprint error */
868     if (err_msg)
869     {
870         hildon_banner_show_information (GTK_WIDGET (GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET(editor),
871                                         GTK_TYPE_WINDOW))), NULL, err_msg);
872         g_free(err_msg);
873     }
874
875     return TRUE;
876 }
877
878 /**
879  * hildon_number_editor_new:
880  * @min: minimum accepted value
881  * @max: maximum accepted value
882  * 
883  * Creates new number editor
884  *
885  * Returns: a new #HildonNumberEditor widget
886  */
887 GtkWidget*
888 hildon_number_editor_new                        (gint min, 
889                                                  gint max)
890 {
891     HildonNumberEditor *editor = g_object_new (HILDON_TYPE_NUMBER_EDITOR, NULL);
892
893     /* Set user inputted range to editor */
894     hildon_number_editor_set_range (editor, min, max);
895
896     return GTK_WIDGET (editor);
897 }
898
899 /**
900  * hildon_number_editor_set_range:
901  * @editor: a #HildonNumberEditor widget
902  * @min: minimum accepted value
903  * @max: maximum accepted value
904  *
905  * Sets accepted number range for editor
906  */
907 void
908 hildon_number_editor_set_range                  (HildonNumberEditor *editor, 
909                                                  gint min, 
910                                                  gint max)
911 {
912     HildonNumberEditorPrivate *priv;
913     gchar buffer_min[32], buffer_max[32];
914     gint a, b;
915
916     g_return_if_fail (HILDON_IS_NUMBER_EDITOR (editor));
917
918     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
919     g_assert (priv);
920
921     /* Set preferences */
922     priv->start = MIN (min, max);
923     priv->end = MAX (min, max);
924
925     /* Find maximum allowed length of value */
926     g_snprintf (buffer_min, sizeof (buffer_min), "%d", min);
927     g_snprintf (buffer_max, sizeof (buffer_max), "%d", max);
928     a = strlen (buffer_min);
929     b = strlen (buffer_max);
930
931     /* Set maximum size of entry */
932     gtk_entry_set_width_chars (GTK_ENTRY (priv->num_entry), MAX (a, b));
933     hildon_number_editor_set_value (editor, priv->start);
934 }
935
936 /**
937  * hildon_number_editor_get_value:
938  * @editor: pointer to #HildonNumberEditor
939  *
940  * Returns: current NumberEditor value
941  */
942 gint
943 hildon_number_editor_get_value                  (HildonNumberEditor *editor)
944 {
945     HildonNumberEditorPrivate *priv;
946
947     g_return_val_if_fail (HILDON_IS_NUMBER_EDITOR (editor), 0);
948
949     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
950     g_assert (priv);
951
952     return atoi (gtk_entry_get_text (GTK_ENTRY (priv->num_entry)));
953 }
954
955 /**
956  * hildon_number_editor_set_value:
957  * @editor: pointer to #HildonNumberEditor
958  * @value: numeric value for number editor
959  *
960  * Sets numeric value for number editor
961  */
962 void
963 hildon_number_editor_set_value                  (HildonNumberEditor *editor, 
964                                                  gint value)
965 {
966     HildonNumberEditorPrivate *priv;
967
968     g_return_if_fail (HILDON_IS_NUMBER_EDITOR (editor));
969
970     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
971     g_assert (priv);
972
973     g_return_if_fail (value <= priv->end);
974     g_return_if_fail (value >= priv->start);
975
976     priv->default_val = value;
977     hildon_number_editor_real_set_value (priv, value);
978     g_object_notify (G_OBJECT(editor), "value");
979 }
980
981 /* When calling gtk_entry_set_text, the entry widget does things that can
982  * cause the whole widget to redraw. This redrawing is delayed and if any
983  * selections are made right after calling the gtk_entry_set_text the
984  * setting of the selection might seem to have no effect.
985  *
986  * If the selection is delayed with a lower priority than the redrawing,
987  * the selection should stick. Calling this function with g_idle_add should
988  * do it.
989  */
990 static gboolean
991 hildon_number_editor_select_all                 (HildonNumberEditorPrivate *priv)
992 {   
993     GDK_THREADS_ENTER ();
994     gtk_editable_select_region (GTK_EDITABLE (priv->num_entry), 0, -1);
995     priv->select_all_idle_id = 0;
996     GDK_THREADS_LEAVE ();
997     return FALSE;
998
999
1000 static void
1001 hildon_number_editor_set_property               (GObject *object,
1002                                                  guint prop_id,
1003                                                  const GValue *value, 
1004                                                  GParamSpec *pspec)
1005 {
1006     HildonNumberEditor *editor;
1007
1008     editor = HILDON_NUMBER_EDITOR (object);
1009
1010     switch (prop_id) {
1011
1012         case PROP_VALUE:
1013             hildon_number_editor_set_value (editor, g_value_get_int (value));
1014             break;
1015
1016         default:
1017             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1018             break;
1019     }
1020 }
1021
1022 static void
1023 hildon_number_editor_get_property               (GObject *object,
1024                                                  guint prop_id, 
1025                                                  GValue *value, 
1026                                                  GParamSpec *pspec)
1027 {
1028     HildonNumberEditor *editor;
1029
1030     editor = HILDON_NUMBER_EDITOR (object);
1031
1032     switch (prop_id) {
1033
1034         case PROP_VALUE:
1035             g_value_set_int(value, hildon_number_editor_get_value (editor));
1036             break;
1037
1038         default:
1039             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1040             break;
1041     }
1042 }
1043