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