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