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