Make sure that all timeouts in HildonBanner are removed
[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 = gdk_threads_add_timeout (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 = gdk_threads_add_timeout (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     /* Update value based on button held */
540     change_numbers (editor, priv->button_type);
541
542     return TRUE;
543 }
544
545 /* Changes the current number value by the amount of update
546    and verifies the result. */
547 static void
548 change_numbers                                  (HildonNumberEditor *editor, 
549                                                  gint update)
550 {
551     HildonNumberEditorPrivate *priv;
552     gint current_value;
553
554     g_assert (HILDON_IS_NUMBER_EDITOR (editor));
555
556     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
557     g_assert (priv);
558
559     current_value = hildon_number_editor_get_value (editor);
560
561     /* We need to rerun validation by hand, since validation
562        done in "changed" callback allows intermediate values */
563     hildon_number_editor_real_set_value (priv, current_value + update);
564     hildon_number_editor_validate_value (editor, FALSE);
565     g_object_notify (G_OBJECT (editor), "value");
566 }
567
568 static void
569 add_select_all_idle                             (HildonNumberEditorPrivate *priv)
570 {
571     g_assert (priv);
572
573     if (! priv->select_all_idle_id)
574     {
575         priv->select_all_idle_id =
576             gdk_threads_add_idle ((GSourceFunc) hildon_number_editor_select_all, priv);
577     }    
578 }
579
580 static void
581 hildon_number_editor_validate_value             (HildonNumberEditor *editor, 
582                                                  gboolean allow_intermediate)
583 {
584     HildonNumberEditorPrivate *priv;
585     gint error_code, fixup_value;
586     const gchar *text;
587     long value;
588     gchar *tail;
589     gboolean r;
590
591     g_assert (HILDON_IS_NUMBER_EDITOR(editor));
592
593     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
594     g_assert (priv);
595
596     text = gtk_entry_get_text (GTK_ENTRY (priv->num_entry));
597     error_code = -1;
598     fixup_value = priv->default_val;
599
600     if (text && text[0])
601     { 
602         /* Try to convert entry text to number */
603         value = strtol (text, &tail, 10);
604
605         /* Check if conversion succeeded */
606         if (tail[0] == 0)
607         {    
608             /* Check if value is in allowed range. This is tricky in those
609                cases when user is editing a value. 
610                For example: Range = [100, 500] and user have just inputted "4".
611                This should not lead into error message. Otherwise value is
612                resetted back to "100" and next "4" press will reset it back
613                and so on. */
614             if (allow_intermediate)
615             {
616                 /* We now have the following error cases:
617                  * If inputted value as above maximum and
618                  maximum is either positive or then maximum
619                  negative and value is positive.
620                  * If inputted value is below minimum and minimum
621                  is negative or minumum positive and value
622                  negative or zero.
623                  In all other cases situation can be fixed just by
624                  adding new numbers to the string.
625                  */
626                 if (value > priv->end && (priv->end >= 0 || (priv->end < 0 && value >= 0)))
627                 {
628                     error_code = HILDON_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED;
629                     fixup_value = priv->end;
630                 }
631                 else if (value < priv->start && (priv->start < 0 || (priv->start >= 0 && value <= 0)))
632                 {
633                     error_code = HILDON_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED;
634                     fixup_value = priv->start;
635                 }
636             }
637             else
638             {
639                 if (value > priv->end) {
640                     error_code = HILDON_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED;
641                     fixup_value = priv->end;
642                 }
643                 else if (value < priv->start) {
644                     error_code = HILDON_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED;
645                     fixup_value = priv->start;
646                 }
647             }
648         }
649         /* The only valid case when conversion can fail is when we
650            have plain '-', intermediate forms are allowed AND
651            minimum bound is negative */
652         else if (! allow_intermediate || strcmp (text, "-") != 0 || priv->start >= 0)
653             error_code = HILDON_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE;
654     }
655     else if (! allow_intermediate)
656         error_code = HILDON_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE;
657
658     if (error_code != -1)
659     {
660         /* If entry is empty and intermediate forms are nor allowed, 
661            emit error signal */
662         /* Change to default value */
663         hildon_number_editor_set_value (editor, fixup_value);
664         g_signal_emit (editor, HildonNumberEditor_signal[RANGE_ERROR], 0, error_code, &r);
665         add_select_all_idle (priv);
666     }
667 }
668
669 static void 
670 hildon_number_editor_entry_changed              (GtkWidget *widget, 
671                                                  gpointer data)
672 {
673     g_assert (HILDON_IS_NUMBER_EDITOR (data));
674     hildon_number_editor_validate_value (HILDON_NUMBER_EDITOR (data), TRUE);
675     g_object_notify (G_OBJECT (data), "value");
676 }
677
678 static void
679 hildon_number_editor_size_request               (GtkWidget *widget,
680                                                  GtkRequisition *requisition)
681 {
682     HildonNumberEditor *editor;
683     HildonNumberEditorPrivate *priv;
684     GtkRequisition req;
685
686     editor = HILDON_NUMBER_EDITOR (widget);
687     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
688     g_assert (priv);
689
690     /* Requested size is size of all child widgets plus border space */
691     gtk_widget_size_request (priv->minus, &req);
692     requisition->width = req.width;
693
694     gtk_widget_size_request (priv->num_entry, &req);
695     requisition->width += req.width;
696
697     gtk_widget_size_request (priv->plus, &req);
698     requisition->width += req.width;
699
700     requisition->width += HILDON_MARGIN_DEFAULT * 2;
701
702     /* FIXME: XXX Height is fixed */
703     requisition->height = NUMBER_EDITOR_HEIGHT;
704 }
705
706 /* Update alloc->width so widget fits, update alloc->x to point to free space */
707 static void
708 set_widget_allocation                           (GtkWidget *widget, 
709                                                  GtkAllocation *alloc,
710                                                  const GtkAllocation *allocation)
711 {
712     GtkRequisition child_requisition;
713
714     gtk_widget_get_child_requisition (widget, &child_requisition);
715
716     /* Fit to widget width */
717     if (allocation->width + allocation->x > alloc->x + child_requisition.width)
718         alloc->width = child_requisition.width;
719     else
720     {
721         alloc->width = allocation->width - (alloc->x - allocation->x);
722         if (alloc->width < 0)
723             alloc->width = 0;
724     }
725
726     gtk_widget_size_allocate (widget, alloc);
727     /* Update x position */
728     alloc->x += alloc->width;
729 }
730
731 static void
732 hildon_number_editor_size_allocate              (GtkWidget *widget,
733                                                  GtkAllocation *allocation)
734 {
735     HildonNumberEditor *editor;
736     HildonNumberEditorPrivate *priv;
737     GtkAllocation alloc;
738
739     editor = HILDON_NUMBER_EDITOR (widget);
740     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
741     g_assert (priv);
742
743     widget->allocation = *allocation;
744
745     /* Add upper border */
746     alloc.y = widget->allocation.y + widget->style->ythickness;
747
748     /* Fix height */
749     if (widget->allocation.height > NUMBER_EDITOR_HEIGHT)
750     {
751         alloc.height = NUMBER_EDITOR_HEIGHT - widget->style->ythickness * 2;
752         alloc.y += (widget->allocation.height - NUMBER_EDITOR_HEIGHT) / 2;
753     }
754     else
755         alloc.height = widget->allocation.height - widget->style->ythickness * 2;
756
757     if (alloc.height < 0)
758         alloc.height = 0;
759
760     /* Add left border */
761     alloc.x = allocation->x + widget->style->xthickness;
762
763     /* Allocate positions for widgets (left-to-right) */
764     set_widget_allocation(priv->minus, &alloc, &widget->allocation);
765     alloc.x += HILDON_MARGIN_DEFAULT;
766
767     set_widget_allocation(priv->num_entry, &alloc, &widget->allocation);
768     alloc.x += HILDON_MARGIN_DEFAULT;
769
770     set_widget_allocation(priv->plus, &alloc, &widget->allocation);
771 }
772
773 static gboolean
774 hildon_number_editor_focus                      (GtkWidget *widget,
775                                                  GtkDirectionType direction)
776 {
777   gboolean retval;
778   GtkDirectionType effective_direction;
779
780   g_assert (HILDON_IS_NUMBER_EDITOR (widget));
781
782   retval = hildon_private_composite_focus (widget, direction, &effective_direction);
783
784   if (retval == TRUE)
785     return GTK_WIDGET_CLASS (parent_class)->focus (widget, effective_direction);
786   else
787     return FALSE;
788 }
789
790 static gboolean
791 hildon_number_editor_entry_focusout             (GtkWidget *widget, 
792                                                  GdkEventFocus *event,
793                                                  gpointer data)
794 {
795     g_assert (HILDON_IS_NUMBER_EDITOR(data));
796
797     hildon_number_editor_validate_value (HILDON_NUMBER_EDITOR(data), FALSE);
798     return FALSE;
799 }
800
801 static gboolean
802 hildon_number_editor_entry_keypress             (GtkWidget *widget, 
803                                                  GdkEventKey *event,
804                                                  gpointer data)
805 {
806     GtkEditable *editable;
807     gint cursor_pos;
808
809     g_assert (HILDON_IS_NUMBER_EDITOR (data));
810
811     editable = GTK_EDITABLE (widget);
812     cursor_pos = gtk_editable_get_position (editable);
813
814     switch (event->keyval)
815     {
816         case GDK_Left:
817             /* If the cursor is on the left, try to decrement */
818             if (cursor_pos == 0) {
819                 change_numbers (HILDON_NUMBER_EDITOR (data), -1);
820                 return TRUE;
821             }
822             break;
823
824         case GDK_Right:
825             /* If the cursor is on the right, try to increment */
826             if (cursor_pos >= g_utf8_strlen(gtk_entry_get_text (GTK_ENTRY (widget)), -1))
827             {
828                 change_numbers (HILDON_NUMBER_EDITOR (data), 1);
829                 gtk_editable_set_position(editable, cursor_pos);
830                 return TRUE;
831             }
832             break;
833
834         default:
835             break;
836     };
837
838     return FALSE;
839 }
840
841 static gboolean
842 hildon_number_editor_range_error                (HildonNumberEditor *editor,
843                                                  HildonNumberEditorErrorType type)
844 {
845
846     gint min, max;
847     gchar *err_msg = NULL;
848     HildonNumberEditorPrivate *priv;
849
850     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
851     g_assert (priv);
852
853     min = priv->start;
854     max = priv->end;
855
856     /* Construct error message */
857     switch (type)
858     {
859         case HILDON_NUMBER_EDITOR_ERROR_MAXIMUM_VALUE_EXCEED:
860             err_msg = g_strdup_printf (_("ckct_ib_maximum_value"), max, max);
861             break;
862
863         case HILDON_NUMBER_EDITOR_ERROR_MINIMUM_VALUE_EXCEED:
864             err_msg = g_strdup_printf (_("ckct_ib_minimum_value"), min, min);
865             break;
866
867         case HILDON_NUMBER_EDITOR_ERROR_ERRONEOUS_VALUE:
868             err_msg =
869                 g_strdup_printf (_("ckct_ib_set_a_value_within_range"), min, max);
870             break;
871     }
872
873     /* Infoprint error */
874     if (err_msg)
875     {
876         hildon_banner_show_information (GTK_WIDGET (GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET(editor),
877                                         GTK_TYPE_WINDOW))), NULL, err_msg);
878         g_free(err_msg);
879     }
880
881     return TRUE;
882 }
883
884 /**
885  * hildon_number_editor_new:
886  * @min: minimum accepted value
887  * @max: maximum accepted value
888  * 
889  * Creates new number editor
890  *
891  * Returns: a new #HildonNumberEditor widget
892  */
893 GtkWidget*
894 hildon_number_editor_new                        (gint min, 
895                                                  gint max)
896 {
897     HildonNumberEditor *editor = g_object_new (HILDON_TYPE_NUMBER_EDITOR, NULL);
898
899     /* Set user inputted range to editor */
900     hildon_number_editor_set_range (editor, min, max);
901
902     return GTK_WIDGET (editor);
903 }
904
905 /**
906  * hildon_number_editor_set_range:
907  * @editor: a #HildonNumberEditor widget
908  * @min: minimum accepted value
909  * @max: maximum accepted value
910  *
911  * Sets accepted number range for editor
912  */
913 void
914 hildon_number_editor_set_range                  (HildonNumberEditor *editor, 
915                                                  gint min, 
916                                                  gint max)
917 {
918     HildonNumberEditorPrivate *priv;
919     gchar buffer_min[32], buffer_max[32];
920     gint a, b;
921
922     g_return_if_fail (HILDON_IS_NUMBER_EDITOR (editor));
923
924     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
925     g_assert (priv);
926
927     /* Set preferences */
928     priv->start = MIN (min, max);
929     priv->end = MAX (min, max);
930
931     /* Find maximum allowed length of value */
932     g_snprintf (buffer_min, sizeof (buffer_min), "%d", min);
933     g_snprintf (buffer_max, sizeof (buffer_max), "%d", max);
934     a = strlen (buffer_min);
935     b = strlen (buffer_max);
936
937     /* Set maximum size of entry */
938     gtk_entry_set_width_chars (GTK_ENTRY (priv->num_entry), MAX (a, b));
939     hildon_number_editor_set_value (editor, priv->start);
940 }
941
942 /**
943  * hildon_number_editor_get_value:
944  * @editor: a #HildonNumberEditor
945  *
946  * Gets the current value of the number editor.
947  *
948  * Returns: the current value
949  */
950 gint
951 hildon_number_editor_get_value                  (HildonNumberEditor *editor)
952 {
953     HildonNumberEditorPrivate *priv;
954
955     g_return_val_if_fail (HILDON_IS_NUMBER_EDITOR (editor), 0);
956
957     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
958     g_assert (priv);
959
960     return atoi (gtk_entry_get_text (GTK_ENTRY (priv->num_entry)));
961 }
962
963 /**
964  * hildon_number_editor_set_value:
965  * @editor: a #HildonNumberEditor
966  * @value: numeric value for number editor
967  *
968  * Sets numeric value for number editor
969  */
970 void
971 hildon_number_editor_set_value                  (HildonNumberEditor *editor, 
972                                                  gint value)
973 {
974     HildonNumberEditorPrivate *priv;
975
976     g_return_if_fail (HILDON_IS_NUMBER_EDITOR (editor));
977
978     priv = HILDON_NUMBER_EDITOR_GET_PRIVATE (editor);
979     g_assert (priv);
980
981     g_return_if_fail (value <= priv->end);
982     g_return_if_fail (value >= priv->start);
983
984     priv->default_val = value;
985     hildon_number_editor_real_set_value (priv, value);
986     g_object_notify (G_OBJECT(editor), "value");
987 }
988
989 /* When calling gtk_entry_set_text, the entry widget does things that can
990  * cause the whole widget to redraw. This redrawing is delayed and if any
991  * selections are made right after calling the gtk_entry_set_text the
992  * setting of the selection might seem to have no effect.
993  *
994  * If the selection is delayed with a lower priority than the redrawing,
995  * the selection should stick. Calling this function with g_idle_add should
996  * do it.
997  */
998 static gboolean
999 hildon_number_editor_select_all                 (HildonNumberEditorPrivate *priv)
1000 {   
1001     gtk_editable_select_region (GTK_EDITABLE (priv->num_entry), 0, -1);
1002     priv->select_all_idle_id = 0;
1003     return FALSE;
1004
1005
1006 static void
1007 hildon_number_editor_set_property               (GObject *object,
1008                                                  guint prop_id,
1009                                                  const GValue *value, 
1010                                                  GParamSpec *pspec)
1011 {
1012     HildonNumberEditor *editor;
1013
1014     editor = HILDON_NUMBER_EDITOR (object);
1015
1016     switch (prop_id) {
1017
1018         case PROP_VALUE:
1019             hildon_number_editor_set_value (editor, g_value_get_int (value));
1020             break;
1021
1022         default:
1023             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1024             break;
1025     }
1026 }
1027
1028 static void
1029 hildon_number_editor_get_property               (GObject *object,
1030                                                  guint prop_id, 
1031                                                  GValue *value, 
1032                                                  GParamSpec *pspec)
1033 {
1034     HildonNumberEditor *editor;
1035
1036     editor = HILDON_NUMBER_EDITOR (object);
1037
1038     switch (prop_id) {
1039
1040         case PROP_VALUE:
1041             g_value_set_int(value, hildon_number_editor_get_value (editor));
1042             break;
1043
1044         default:
1045             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1046             break;
1047     }
1048 }
1049