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