Make sure that all timeouts in HildonBanner are removed
[hildon] / hildon / hildon-range-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-range-editor
27  * @short_description: A widget is used to ask bounds of a range.
28  *
29  * HidlonRangeEditor allows entering a pair of integers, e.g. the lower
30  * and higher bounds of a range. A minimum and maximum can also be set
31  * for the bounds.
32  *
33  * <note>
34  *   <para>
35  * #HildonRangeEditor has been deprecated since Hildon 2.2 and should not
36  * be used in newly written code. See
37  * <link linkend="hildon-migrating-number-widgets">Migrating Number Widgets</link>
38  * section to know how to migrate this deprecated widget.
39  *   </para>
40  * </note>
41  *
42  * <example>
43  * <programlisting>
44  *      range_editor = hildon_range_editor_new ();
45  *      hildon_range_editor_set_limits (editor, start, end );
46  *      hildon_range_editor_get_range (editor, &amp;start, &amp;end);
47  * </programlisting>
48  * </example>
49  */
50
51 #undef                                          HILDON_DISABLE_DEPRECATED
52
53 #ifdef                                          HAVE_CONFIG_H
54 #include                                        <config.h>
55 #endif
56
57 #include                                        <libintl.h>
58 #include                                        <string.h>
59 #include                                        <stdlib.h>
60 #include                                        <glib/gprintf.h>
61 #include                                        <gdk/gdkkeysyms.h>
62
63 #include                                        "hildon-range-editor.h"
64 #include                                        "hildon-banner.h"
65 #include                                        "hildon-range-editor-private.h"
66
67 #define                                         _(string) \
68                                                 dgettext("hildon-libs", string)
69
70 /* Alignment in entry box ( 0 = left, 1 = right ) */
71
72 #define                                         DEFAULT_ALIGNMENT 1
73
74 /* Amount of padding to add to each side of the separator */
75
76 #define                                         DEFAULT_PADDING 3
77
78 #define                                         DEFAULT_START -999
79
80 #define                                         DEFAULT_END    999
81
82 #define                                         DEFAULT_LENGTH 4
83
84 static GtkContainerClass                        *parent_class = NULL;
85
86 static void
87 hildon_range_editor_class_init                  (HildonRangeEditorClass *editor_class);
88
89 static void
90 hildon_range_editor_init                        (HildonRangeEditor *editor);
91
92 static void
93 hildon_range_editor_forall                      (GtkContainer *container,
94                                                  gboolean include_internals, 
95                                                  GtkCallback callback,
96                                                  gpointer callback_data);
97
98 static void
99 hildon_range_editor_destroy                     (GtkObject *self);
100
101 static void
102 hildon_range_editor_size_request                (GtkWidget *widget,
103                                                  GtkRequisition *requisition);
104
105 static void
106 hildon_range_editor_size_allocate               (GtkWidget *widget,
107                                                  GtkAllocation *allocation);
108
109 static gboolean
110 hildon_range_editor_entry_focus_in              (GtkEditable *editable,
111                                                  GdkEventFocus *event,
112                                                  HildonRangeEditor *editor);
113
114 static gboolean
115 hildon_range_editor_entry_focus_out             (GtkEditable *editable,
116                                                  GdkEventFocus *event,
117                                                  HildonRangeEditor *editor);
118
119 static gboolean
120 hildon_range_editor_entry_keypress              (GtkWidget *widget, 
121                                                  GdkEventKey *event,
122                                                  HildonRangeEditor *editor);
123
124 static gboolean
125 hildon_range_editor_released                    (GtkEditable *editable, 
126                                                  GdkEventButton *event,
127                                                  HildonRangeEditor *editor);
128
129 static gboolean
130 hildon_range_editor_press                       (GtkEditable *editable, 
131                                                  GdkEventButton *event,
132                                                  HildonRangeEditor *editor);
133
134 static void 
135 hildon_range_editor_set_property                (GObject *object, 
136                                                  guint param_id,
137                                                  const GValue *value, 
138                                                  GParamSpec *pspec);
139
140 static void
141 hildon_range_editor_get_property                (GObject *object, 
142                                                  guint param_id,
143                                                  GValue *value, 
144                                                  GParamSpec *pspec);
145
146 static void
147 hildon_range_editor_entry_changed               (GtkWidget *widget, 
148                                                  HildonRangeEditor *editor);
149
150 enum
151 {
152     PROP_0,
153     PROP_LOWER = 1,
154     PROP_HIGHER,
155     PROP_MIN,
156     PROP_MAX,
157     PROP_SEPARATOR
158 };
159
160 static void
161 hildon_range_editor_class_init                  (HildonRangeEditorClass *editor_class)
162 {
163     GObjectClass *gobject_class = G_OBJECT_CLASS (editor_class);
164     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (editor_class);
165     GtkContainerClass *container_class = GTK_CONTAINER_CLASS (editor_class);
166
167     parent_class = g_type_class_peek_parent (editor_class);
168
169     g_type_class_add_private (editor_class, sizeof (HildonRangeEditorPrivate));
170
171     gobject_class->set_property = hildon_range_editor_set_property;
172     gobject_class->get_property = hildon_range_editor_get_property;
173     widget_class->size_request  = hildon_range_editor_size_request;
174     widget_class->size_allocate = hildon_range_editor_size_allocate;
175
176     container_class->forall = hildon_range_editor_forall;
177     GTK_OBJECT_CLASS (editor_class)->destroy = hildon_range_editor_destroy;
178
179     gtk_widget_class_install_style_property (widget_class,
180             g_param_spec_int ("hildon_range_editor_entry_alignment",
181                 "Hildon RangeEditor entry alignment",
182                 "Hildon RangeEditor entry alignment", 0, 1,
183                 DEFAULT_ALIGNMENT,
184                 G_PARAM_READABLE));
185
186     gtk_widget_class_install_style_property (widget_class,
187             g_param_spec_int ("hildon_range_editor_separator_padding",
188                 "Hildon RangeEditor separator padding",
189                 "Hildon RangeEditor separaror padding",
190                 G_MININT, G_MAXINT,
191                 DEFAULT_PADDING,
192                 G_PARAM_READABLE));
193
194     /**
195      * HildonRangeEditor:min:
196      *
197      * Minimum value in a range.
198      * Default: -999
199      */
200     g_object_class_install_property (gobject_class, PROP_MIN,
201             g_param_spec_int ("min",
202                 "Minimum value",
203                 "Minimum value in a range",
204                 G_MININT, G_MAXINT,
205                 DEFAULT_START, G_PARAM_CONSTRUCT | 
206                 G_PARAM_READABLE | G_PARAM_WRITABLE));
207
208     /**
209      * HildonRangeEditor:max:
210      *
211      * Maximum value in a range.
212      * Default: 999
213      */
214     g_object_class_install_property (gobject_class, PROP_MAX,
215             g_param_spec_int ("max",
216                 "Maximum value",
217                 "Maximum value in a range",
218                 G_MININT, G_MAXINT,
219                 DEFAULT_END, G_PARAM_CONSTRUCT | 
220                 G_PARAM_READABLE | G_PARAM_WRITABLE));
221
222     /**
223      * HildonRangeEditor:lower:
224      *
225      * Current value in the entry presenting lower end of selected range.
226      * Default: -999
227      */
228     g_object_class_install_property (gobject_class, PROP_LOWER,
229             g_param_spec_int ("lower",
230                 "Current lower value",
231                 "Current value in the entry presenting lower end of selected range",
232                 G_MININT, G_MAXINT,
233                 DEFAULT_START, G_PARAM_CONSTRUCT | 
234                 G_PARAM_READABLE | G_PARAM_WRITABLE));
235
236     /**
237      * HildonRangeEditor:higher:
238      *
239      * Current value in the entry presenting higher end of selected range.
240      * Default: 999
241      */
242     g_object_class_install_property (gobject_class, PROP_HIGHER,
243             g_param_spec_int ("higher",
244                 "Current higher value",
245                 "Current value in the entry presenting higher end of selected range",
246                 G_MININT, G_MAXINT,
247                 DEFAULT_END, G_PARAM_CONSTRUCT | 
248                 G_PARAM_READABLE | G_PARAM_WRITABLE));
249
250     /**
251      * HildonRangeEditor:separator:
252      *
253      * Separator string to separate range editor entries.
254      * Default: "-"
255      */
256     g_object_class_install_property (gobject_class, PROP_SEPARATOR,
257             g_param_spec_string ("separator",
258                 "Separator",
259                 "Separator string to separate entries",
260                 _("ckct_wi_range_separator"),
261                 G_PARAM_CONSTRUCT | 
262                 G_PARAM_READABLE | G_PARAM_WRITABLE));
263 }
264
265 static void
266 hildon_range_editor_init                        (HildonRangeEditor *editor)
267 {
268     HildonRangeEditorPrivate *priv;
269
270     gint range_editor_entry_alignment;
271     gint range_editor_separator_padding;
272
273     priv = HILDON_RANGE_EDITOR_GET_PRIVATE (editor);
274     g_assert (priv);
275
276     GTK_WIDGET_SET_FLAGS (editor, GTK_NO_WINDOW);
277
278     gtk_widget_push_composite_child ();
279
280     priv->start_entry = gtk_entry_new ();
281     priv->end_entry = gtk_entry_new ();
282     priv->label = gtk_label_new (_("ckct_wi_range_separator"));
283     priv->bp = FALSE;
284
285     /* Get values from gtkrc (or use defaults) */
286     /* FIXME: This is broken, styles are not yet attached */
287     gtk_widget_style_get (GTK_WIDGET (editor),
288             "hildon_range_editor_entry_alignment",
289             &range_editor_entry_alignment,
290             "hildon_range_editor_separator_padding",
291             &range_editor_separator_padding, NULL);
292
293     /* Add padding to separator */
294     gtk_misc_set_padding (GTK_MISC (priv->label),
295             range_editor_separator_padding, 0);
296
297     /* Align the text to right in entry box */
298     gtk_entry_set_alignment (GTK_ENTRY (priv->start_entry),
299             range_editor_entry_alignment);
300     gtk_entry_set_alignment (GTK_ENTRY (priv->end_entry),
301             range_editor_entry_alignment);
302
303     gtk_widget_set_composite_name (priv->start_entry, "start_entry");
304     gtk_widget_set_composite_name (priv->end_entry, "end_entry");
305     gtk_widget_set_composite_name (priv->label, "separator_label");
306     gtk_widget_set_parent (priv->start_entry, GTK_WIDGET (editor));
307     gtk_widget_set_parent (priv->end_entry, GTK_WIDGET (editor));
308     gtk_widget_set_parent (priv->label, GTK_WIDGET (editor));
309
310     g_signal_connect (G_OBJECT (priv->start_entry), "button-release-event",
311             G_CALLBACK (hildon_range_editor_released), editor);
312     g_signal_connect (G_OBJECT (priv->end_entry), "button-release-event",
313             G_CALLBACK (hildon_range_editor_released), editor);
314
315     g_signal_connect (G_OBJECT (priv->start_entry), "button-press-event",
316             G_CALLBACK (hildon_range_editor_press), editor);
317     g_signal_connect (G_OBJECT (priv->end_entry), "button-press-event",
318             G_CALLBACK (hildon_range_editor_press), editor);
319
320     g_signal_connect (G_OBJECT (priv->start_entry), "key-press-event",
321             G_CALLBACK (hildon_range_editor_entry_keypress), editor);
322     g_signal_connect (G_OBJECT (priv->end_entry), "key-press-event",
323             G_CALLBACK (hildon_range_editor_entry_keypress), editor);
324
325     g_signal_connect (G_OBJECT (priv->start_entry), "focus-in-event",
326             G_CALLBACK (hildon_range_editor_entry_focus_in), editor);
327     g_signal_connect (G_OBJECT (priv->end_entry), "focus-in-event",
328             G_CALLBACK (hildon_range_editor_entry_focus_in), editor);
329
330     g_signal_connect (G_OBJECT (priv->start_entry), "focus-out-event",
331             G_CALLBACK (hildon_range_editor_entry_focus_out), editor);
332     g_signal_connect (G_OBJECT (priv->end_entry), "focus-out-event",
333             G_CALLBACK (hildon_range_editor_entry_focus_out), editor);
334     g_signal_connect (priv->start_entry, "changed", 
335             G_CALLBACK (hildon_range_editor_entry_changed), editor);
336     g_signal_connect (priv->end_entry, "changed", 
337             G_CALLBACK (hildon_range_editor_entry_changed), editor);
338
339 #ifdef MAEMO_GTK 
340     g_object_set (G_OBJECT (priv->start_entry),
341             "hildon-input-mode", HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
342
343     g_object_set( G_OBJECT (priv->end_entry),
344             "hildon-input-mode", HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
345 #endif 
346
347     gtk_widget_show (priv->start_entry);
348     gtk_widget_show (priv->end_entry);
349     gtk_widget_show (priv->label);
350
351     gtk_widget_pop_composite_child();
352 }
353
354 static void 
355 hildon_range_editor_set_property                (GObject *object, 
356                                                  guint param_id,
357                                                  const GValue *value, 
358                                                  GParamSpec *pspec)
359 {
360     HildonRangeEditor *editor = HILDON_RANGE_EDITOR(object);
361
362     switch (param_id)
363     {
364         case PROP_LOWER:
365             hildon_range_editor_set_lower (editor, g_value_get_int (value));
366             break;
367
368         case PROP_HIGHER:
369             hildon_range_editor_set_higher (editor, g_value_get_int (value));
370             break;
371
372         case PROP_MIN:
373             hildon_range_editor_set_min (editor, g_value_get_int (value));
374             break;
375
376         case PROP_MAX:
377             hildon_range_editor_set_max (editor, g_value_get_int (value));
378             break;
379
380         case PROP_SEPARATOR:
381             hildon_range_editor_set_separator (editor,
382                     g_value_get_string (value));
383             break;
384
385         default:
386             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
387             break;
388     }
389 }
390
391 static void
392 hildon_range_editor_get_property                (GObject *object, 
393                                                  guint param_id,
394                                                  GValue *value, 
395                                                  GParamSpec *pspec)
396 {
397     HildonRangeEditor *editor = HILDON_RANGE_EDITOR(object);
398
399     switch (param_id)
400     {
401         case PROP_LOWER:
402             g_value_set_int (value, hildon_range_editor_get_lower (editor));
403             break;
404
405         case PROP_HIGHER:
406             g_value_set_int (value, hildon_range_editor_get_higher (editor));
407             break;
408
409         case PROP_MIN:
410             g_value_set_int (value, hildon_range_editor_get_min (editor));
411             break;
412
413         case PROP_MAX:
414             g_value_set_int (value, hildon_range_editor_get_max (editor));
415             break;
416
417         case PROP_SEPARATOR:
418             g_value_set_string (value, hildon_range_editor_get_separator (editor));
419             break;
420
421         default:
422             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
423             break;
424     }
425 }
426
427 static void
428 hildon_range_editor_entry_validate              (HildonRangeEditor *editor, 
429                                                  GtkWidget *edited_entry, 
430                                                  gboolean allow_intermediate)
431 {
432     HildonRangeEditorPrivate *priv;
433     const gchar *text;
434     long value;
435     gint min, max, fixup;
436     gchar *tail;
437     gchar buffer[256];
438     gboolean error = FALSE;
439
440     g_assert(HILDON_IS_RANGE_EDITOR(editor));
441     g_assert(GTK_IS_ENTRY(edited_entry));
442
443     priv = HILDON_RANGE_EDITOR_GET_PRIVATE(editor);
444     g_assert (priv);
445
446     /* Find the valid range for the modified component */
447     if (edited_entry == priv->start_entry) {
448         min = hildon_range_editor_get_min (editor);
449         max = hildon_range_editor_get_higher (editor);
450     } else {
451         min = hildon_range_editor_get_lower (editor);
452         max = hildon_range_editor_get_max (editor);
453     }
454
455     text = gtk_entry_get_text (GTK_ENTRY (edited_entry));
456
457     if (text && text [0])
458     { 
459         /* Try to convert entry text to number */
460         value = strtol(text, &tail, 10);
461
462         /* Check if conversion succeeded */
463         if (tail [0] == 0)
464         {    
465             /* Check if value is in allowed range. This is tricky in those
466                cases when user is editing a value. 
467                For example: Range = [100, 500] and user have just inputted "4".
468                This should not lead into error message. Otherwise value is
469                resetted back to "100" and next "4" press will reset it back
470                and so on. */
471             if (allow_intermediate)
472             {
473                 /* We now have the following error cases:
474                  * If inputted value as above maximum and
475                  maximum is either positive or then maximum
476                  negative and value is positive.
477                  * If inputted value is below minimum and minimum
478                  is negative or minumum positive and value
479                  negative.
480                  In all other cases situation can be fixed just by
481                  adding new numbers to the string.
482                  */
483                 if (value > max && (max >= 0 || (max < 0 && value >= 0)))
484                 {
485                     error = TRUE;
486                     fixup = max;
487                     g_snprintf (buffer, sizeof (buffer), _("ckct_ib_maximum_value"), max);
488                 }
489                 else if (value < min && (min < 0 || (min >= 0 && value < 0)))
490                 {
491                     error = TRUE;
492                     fixup = min;
493                     g_snprintf (buffer, sizeof (buffer), _("ckct_ib_minimum_value"), min);
494                 }
495             }
496             else
497             {
498                 if (value > max) {
499                     error = TRUE;
500                     fixup = max;
501                     g_snprintf (buffer, sizeof (buffer), _("ckct_ib_maximum_value"), max);
502                 }
503                 else if (value < min) {
504                     error = TRUE;
505                     fixup = min;
506                     g_snprintf (buffer, sizeof (buffer), _("ckct_ib_minimum_value"), min);
507                 }
508             }
509
510             if (error) {
511                 if (edited_entry == priv->start_entry)
512                     hildon_range_editor_set_lower (editor, fixup);
513                 else
514                     hildon_range_editor_set_higher (editor, fixup);
515             }
516         }
517         /* The only valid case when conversion can fail is when we
518            have plain '-', intermediate forms are allowed AND
519            minimum bound is negative */
520         else if (!allow_intermediate || strcmp(text, "-") != 0 || min >= 0) {
521             error = TRUE;
522             g_snprintf (buffer, sizeof (buffer), _("ckct_ib_set_a_value_within_range"), min, max);
523         }
524     }
525     else if (! allow_intermediate) {
526         error = TRUE;
527         g_snprintf (buffer, sizeof (buffer), _("ckct_ib_set_a_value_within_range"), min, max);
528     }
529
530     if (error)
531     {
532         hildon_banner_show_information (edited_entry, NULL, buffer);
533         gtk_widget_grab_focus (edited_entry);
534     }
535 }
536
537 static gboolean
538 hildon_range_editor_entry_focus_in              (GtkEditable *editable,
539                                                  GdkEventFocus *event,
540                                                  HildonRangeEditor *editor)
541 {
542     HildonRangeEditorPrivate *priv = HILDON_RANGE_EDITOR_GET_PRIVATE(editor);
543     g_assert (priv);
544
545     if (priv->bp)
546     {
547         priv->bp = FALSE;
548         return FALSE;
549     }
550
551     if (GTK_WIDGET (editable) == priv->start_entry)
552         gtk_editable_select_region (editable, -1, 0);
553     else
554         gtk_editable_select_region (editable, 0, -1);
555     return FALSE;
556 }
557
558 /* Gets and sets the current range. This has two useful side effects:
559  * Values are now sorted to the correct order
560  * Out of range values are clamped to range */
561 static void 
562 hildon_range_editor_apply_current_range         (HildonRangeEditor *editor)
563 {
564     g_assert (HILDON_IS_RANGE_EDITOR (editor));
565
566     hildon_range_editor_set_range (editor,
567             hildon_range_editor_get_lower (editor),
568             hildon_range_editor_get_higher (editor));
569 }
570
571 static void 
572 hildon_range_editor_entry_changed               (GtkWidget *widget, 
573                                                  HildonRangeEditor *editor)
574 {
575     g_assert (HILDON_IS_RANGE_EDITOR (editor));
576
577     hildon_range_editor_entry_validate (editor, widget, TRUE);
578 }
579
580 static gboolean
581 hildon_range_editor_entry_focus_out             (GtkEditable *editable,
582                                                  GdkEventFocus *event,
583                                                  HildonRangeEditor *editor)
584 {
585     g_assert (HILDON_IS_RANGE_EDITOR(editor));
586
587     hildon_range_editor_entry_validate (editor, GTK_WIDGET (editable), FALSE);  
588     return FALSE;
589 }
590
591 static gboolean
592 hildon_range_editor_press                       (GtkEditable *editable, 
593                                                  GdkEventButton *event,
594                                                  HildonRangeEditor *editor)
595 {
596     HildonRangeEditorPrivate *priv = HILDON_RANGE_EDITOR_GET_PRIVATE(editor);
597     g_assert (priv);
598
599     priv->bp = TRUE;
600     return FALSE;
601 }
602
603 static void
604 hildon_range_editor_forall                      (GtkContainer *container,
605                                                  gboolean include_internals,
606                                                  GtkCallback callback, 
607                                                  gpointer callback_data)
608 {
609     HildonRangeEditorPrivate *priv;
610
611     g_assert (HILDON_IS_RANGE_EDITOR (container));
612     g_assert (callback != NULL);
613
614     priv = HILDON_RANGE_EDITOR_GET_PRIVATE (container);
615     g_assert (priv);
616
617     if (! include_internals)
618         return;
619
620     (*callback) (priv->start_entry, callback_data);
621     (*callback) (priv->end_entry, callback_data);
622     (*callback) (priv->label, callback_data);
623 }
624
625 static void
626 hildon_range_editor_destroy                     (GtkObject *self)
627 {
628     HildonRangeEditorPrivate *priv = HILDON_RANGE_EDITOR_GET_PRIVATE (self);
629
630     if (priv->start_entry)
631     {
632         gtk_widget_unparent (priv->start_entry);
633         priv->start_entry = NULL;
634     }
635     if (priv->end_entry)
636     {
637         gtk_widget_unparent (priv->end_entry);
638         priv->end_entry = NULL;
639     }
640     if (priv->label)
641     {
642         gtk_widget_unparent (priv->label);
643         priv->label = NULL;
644     }
645
646     if (GTK_OBJECT_CLASS (parent_class)->destroy)
647         GTK_OBJECT_CLASS (parent_class)->destroy (self);
648 }
649
650
651 static void
652 hildon_range_editor_size_request                (GtkWidget *widget,
653                                                  GtkRequisition *requisition)
654 {
655     HildonRangeEditorPrivate *priv = NULL;
656     GtkRequisition lab_req, mreq;
657
658     priv = HILDON_RANGE_EDITOR_GET_PRIVATE (widget);
659     g_assert (priv);
660
661     gtk_entry_get_width_chars (GTK_ENTRY (priv->end_entry));
662
663     gtk_widget_size_request (priv->start_entry, &mreq);
664     gtk_widget_size_request (priv->end_entry, &mreq);
665     gtk_widget_size_request (priv->label, &lab_req);
666
667     /* Width for entries and separator label and border */
668     requisition->width = mreq.width * 2 + lab_req.width +
669         widget->style->xthickness * 2;
670     /* Add vertical border */
671     requisition->height = mreq.height + widget->style->ythickness * 2;
672     /* Fit label height */
673     requisition->height = MAX (requisition->height, lab_req.height);
674 }
675
676 static void
677 hildon_range_editor_size_allocate               (GtkWidget *widget,
678                                                  GtkAllocation *allocation)
679 {
680     HildonRangeEditorPrivate *priv;
681     GtkAllocation child1_allocation = {0};
682     GtkAllocation child2_allocation = {0};
683     GtkAllocation child3_allocation = {0};
684
685     priv = HILDON_RANGE_EDITOR_GET_PRIVATE (widget);
686     g_assert (priv);
687
688     widget->allocation = *allocation;
689
690     /* Allocate entries, left-to-right */
691     if (priv->start_entry && GTK_WIDGET_VISIBLE (priv->start_entry))
692     {
693         GtkRequisition child_requisition;
694
695         gtk_widget_get_child_requisition (priv->start_entry,
696                 &child_requisition);
697
698         child1_allocation.x = allocation->x;
699         child1_allocation.y = allocation->y;
700
701         child1_allocation.width = child_requisition.width;
702         child1_allocation.height = allocation->height;
703
704         gtk_widget_size_allocate (priv->start_entry, &child1_allocation);
705     }
706
707     if (priv->label && GTK_WIDGET_VISIBLE (priv->label))
708     {
709         GtkRequisition child_requisition;
710
711         gtk_widget_get_child_requisition (priv->label, &child_requisition);
712
713         child2_allocation.x = child1_allocation.x + child1_allocation.width;
714         child2_allocation.y = allocation->y;
715         /* Add spacing */
716         child2_allocation.width = child_requisition.width + 4;
717         child2_allocation.height = allocation->height;
718
719         gtk_widget_size_allocate (priv->label, &child2_allocation);
720     }
721
722     if (priv->end_entry && GTK_WIDGET_VISIBLE (priv->end_entry))
723     {
724         GtkRequisition child_requisition;
725
726         gtk_widget_get_child_requisition (priv->end_entry, &child_requisition);
727
728         child3_allocation.x = child2_allocation.x + child2_allocation.width;
729         child3_allocation.y = allocation->y;
730
731         child3_allocation.width = child_requisition.width;
732         child3_allocation.height = allocation->height;
733
734         gtk_widget_size_allocate (priv->end_entry, &child3_allocation);
735     }
736 }
737
738 /* Button released inside entries */
739 static gboolean
740 hildon_range_editor_released                    (GtkEditable *editable, 
741                                                  GdkEventButton *event,
742                                                  HildonRangeEditor *editor)
743 {
744     HildonRangeEditorPrivate *priv = HILDON_RANGE_EDITOR_GET_PRIVATE(editor);
745     g_assert (priv);
746
747     if (GTK_WIDGET (editable) == priv->start_entry)
748         gtk_editable_select_region(editable, -1, 0);
749     else
750         gtk_editable_select_region(editable, 0, -1); 
751
752     return FALSE;
753 }
754
755 static gboolean
756 hildon_range_editor_entry_keypress              (GtkWidget *widget, 
757                                                  GdkEventKey *event,
758                                                  HildonRangeEditor *editor)
759 {
760     const gchar *text;
761     gint cursor_pos;
762
763     g_assert(HILDON_IS_RANGE_EDITOR (editor));
764
765     text = gtk_entry_get_text (GTK_ENTRY (widget));
766     cursor_pos = gtk_editable_get_position (GTK_EDITABLE (widget));
767
768     switch (event->keyval)
769     {
770         case GDK_Left:
771             /* If we are on the first character and press left, 
772                try to move to previous field */
773             if (cursor_pos == 0) {
774                 (void) gtk_widget_child_focus (GTK_WIDGET (editor), GTK_DIR_LEFT);
775                 return TRUE;
776             }
777             break;
778
779         case GDK_Right:
780             /* If the cursor is on the right, try to move to the next field */
781             if (cursor_pos >= g_utf8_strlen (text, -1)) {
782                 (void) gtk_widget_child_focus (GTK_WIDGET (editor), GTK_DIR_RIGHT);
783                 return TRUE;
784             }
785             break;
786
787         default:
788             break;
789     };
790
791     return FALSE;
792 }
793
794 static void 
795 hildon_range_editor_refresh_widths              (HildonRangeEditorPrivate *priv)
796 {
797     gchar start_range[32], end_range[32];
798     gint length;
799
800     /* Calculate length of entry so extremes would fit */
801     g_snprintf (start_range, sizeof (start_range), "%d", priv->range_limits_start);
802     g_snprintf (end_range, sizeof (end_range), "%d", priv->range_limits_end);
803     length = MAX (g_utf8_strlen (start_range, -1), g_utf8_strlen (end_range, -1));
804
805     gtk_entry_set_width_chars (GTK_ENTRY (priv->start_entry), length);
806     gtk_entry_set_max_length (GTK_ENTRY (priv->start_entry), length);
807     gtk_entry_set_width_chars (GTK_ENTRY (priv->end_entry), length);
808     gtk_entry_set_max_length (GTK_ENTRY (priv->end_entry), length);
809 }
810
811 /**
812  * hildon_range_editor_get_type:
813  * 
814  * Initializes, and returns the type of a hildon range editor.
815  * 
816  * Returns: GType of #HildonRangeEditor
817  * 
818  */
819 GType G_GNUC_CONST
820 hildon_range_editor_get_type                    (void)
821 {
822     static GType editor_type = 0;
823
824     if (! editor_type)
825     {
826         static const GTypeInfo editor_info =
827         {
828             sizeof (HildonRangeEditorClass),
829             NULL,       /* base_init */
830             NULL,       /* base_finalize */
831             (GClassInitFunc) hildon_range_editor_class_init,
832             NULL,       /* class_finalize */
833             NULL,       /* class_data */
834             sizeof (HildonRangeEditor),
835             0,  /* n_preallocs */
836             (GInstanceInitFunc) hildon_range_editor_init,
837         };
838         editor_type = g_type_register_static (GTK_TYPE_CONTAINER,
839                 "HildonRangeEditor",
840                 &editor_info, 0);
841     }
842     return editor_type;
843 }
844
845 /**
846  * hildon_range_editor_new:
847  *
848  * HildonRangeEditor contains two GtkEntrys that accept numbers and minus. 
849  *
850  * Returns: pointer to a new @HildonRangeEditor widget
851  */
852 GtkWidget*
853 hildon_range_editor_new                         (void)
854 {
855     return GTK_WIDGET (g_object_new (HILDON_TYPE_RANGE_EDITOR, NULL));
856 }
857
858 /**
859  * hildon_range_editor_new_with_separator:
860  * @separator: a string that is shown between the numbers
861  *
862  * HildonRangeEditor contains two Gtk entries that accept numbers. 
863  * A separator is displayed between two entries. 
864  * CHECKME: Use '-' as a separator in the case of null separator?
865  * 
866  * Returns: pointer to a new @HildonRangeEditor widget
867  */
868 GtkWidget*
869 hildon_range_editor_new_with_separator          (const gchar *separator)
870 {
871     return GTK_WIDGET (g_object_new (HILDON_TYPE_RANGE_EDITOR,
872                 "separator", separator, NULL));
873 }
874
875 /**
876  * hildon_range_editor_set_range:
877  * @editor: the #HildonRangeEditor widget
878  * @start: range's start value 
879  * @end: range's end value
880  *
881  * Sets a range to the editor. (The current value)
882  *
883  * Sets the range of the @HildonRangeEditor widget.
884  */
885 void
886 hildon_range_editor_set_range                   (HildonRangeEditor *editor, 
887                                                  gint start,
888                                                  gint end)
889 {
890     g_return_if_fail (HILDON_IS_RANGE_EDITOR (editor));
891
892     /* Make sure that the start/end appear in the correct order */
893     hildon_range_editor_set_lower (editor, MIN (start, end));
894     hildon_range_editor_set_higher (editor, MAX (start, end));
895 }
896
897 /**
898  * hildon_range_editor_get_range:
899  * @editor: the #HildonRangeEditor widget
900  * @start: ranges start value
901  * @end: ranges end value
902  *
903  * Gets the range of the @HildonRangeEditor widget.
904  */
905 void
906 hildon_range_editor_get_range                   (HildonRangeEditor *editor, 
907                                                  gint *start,
908                                                  gint *end)
909 {
910     g_return_if_fail (HILDON_IS_RANGE_EDITOR (editor) && start && end);
911
912     *start = hildon_range_editor_get_lower (editor);
913     *end = hildon_range_editor_get_higher (editor);
914 }
915
916 /**
917  * hildon_range_editor_set_limits:
918  * @editor: the #HildonRangeEditor widget
919  * @start: minimum acceptable value (default: no limit)
920  * @end:   maximum acceptable value (default: no limit)
921  *
922  * Sets the range of the @HildonRangeEditor widget.
923  */
924 void
925 hildon_range_editor_set_limits                  (HildonRangeEditor *editor, 
926                                                  gint start,
927                                                  gint end)
928 {
929     /* FIXME: Setting start/end as separate steps can modify
930        the inputted range unneedlesly */
931     hildon_range_editor_set_min (editor, start);
932     hildon_range_editor_set_max (editor, end);
933 }
934
935 void
936 hildon_range_editor_set_lower                   (HildonRangeEditor *editor, 
937                                                  gint value)
938 {
939     HildonRangeEditorPrivate *priv;
940     gchar buffer[32];
941
942     g_return_if_fail (HILDON_IS_RANGE_EDITOR (editor));
943     priv = HILDON_RANGE_EDITOR_GET_PRIVATE (editor);
944
945     g_snprintf(buffer, sizeof (buffer), "%d", 
946             CLAMP (value, priv->range_limits_start, priv->range_limits_end));
947
948     /* Update entry text with new value */
949     gtk_entry_set_text (GTK_ENTRY (priv->start_entry), buffer);
950     g_object_notify (G_OBJECT (editor), "lower");
951 }
952
953 void
954 hildon_range_editor_set_higher                  (HildonRangeEditor *editor, 
955                                                  gint value)
956 {
957     HildonRangeEditorPrivate *priv;
958     gchar buffer[32];
959
960     g_return_if_fail (HILDON_IS_RANGE_EDITOR (editor));
961     priv = HILDON_RANGE_EDITOR_GET_PRIVATE (editor);
962
963     g_snprintf(buffer, sizeof(buffer), "%d", 
964             CLAMP(value, priv->range_limits_start, priv->range_limits_end));
965
966     /* Update entry text with new value */
967     gtk_entry_set_text (GTK_ENTRY (priv->end_entry), buffer);
968     g_object_notify (G_OBJECT (editor), "higher");
969 }
970
971 gint
972 hildon_range_editor_get_lower                   (HildonRangeEditor *editor)
973 {
974     HildonRangeEditorPrivate *priv;
975     g_return_val_if_fail (HILDON_IS_RANGE_EDITOR (editor), 0);
976     priv = HILDON_RANGE_EDITOR_GET_PRIVATE (editor);
977     return atoi(gtk_entry_get_text(GTK_ENTRY(priv->start_entry)));
978 }
979
980 gint
981 hildon_range_editor_get_higher                  (HildonRangeEditor *editor)
982 {
983     HildonRangeEditorPrivate *priv;
984     g_return_val_if_fail (HILDON_IS_RANGE_EDITOR (editor), 0);
985     priv = HILDON_RANGE_EDITOR_GET_PRIVATE (editor);
986     return atoi (gtk_entry_get_text(GTK_ENTRY (priv->end_entry)));
987 }
988
989 void
990 hildon_range_editor_set_min                     (HildonRangeEditor *editor, 
991                                                  gint value)
992 {
993     HildonRangeEditorPrivate *priv;
994
995     g_return_if_fail (HILDON_IS_RANGE_EDITOR (editor));
996
997     /* We can cause many properties to change */
998     g_object_freeze_notify (G_OBJECT(editor));
999     priv = HILDON_RANGE_EDITOR_GET_PRIVATE (editor);
1000     priv->range_limits_start = value;
1001
1002     if (priv->range_limits_end < value)
1003         hildon_range_editor_set_max (editor, value);
1004     /* Setting maximum applies widths and range in this case */
1005     else {
1006         hildon_range_editor_refresh_widths (priv);
1007         hildon_range_editor_apply_current_range (editor);
1008     }
1009
1010     g_object_notify (G_OBJECT (editor), "min");
1011     g_object_thaw_notify (G_OBJECT (editor));
1012 }
1013
1014 void
1015 hildon_range_editor_set_max                     (HildonRangeEditor *editor, 
1016                                                  gint value)
1017 {
1018     HildonRangeEditorPrivate *priv;
1019
1020     g_return_if_fail (HILDON_IS_RANGE_EDITOR (editor));
1021
1022     /* We can cause many properties to change */
1023     g_object_freeze_notify (G_OBJECT (editor));
1024     priv = HILDON_RANGE_EDITOR_GET_PRIVATE (editor);
1025     priv->range_limits_end = value;
1026
1027     if (priv->range_limits_start > value)
1028         hildon_range_editor_set_min (editor, value);
1029     /* Setting minimum applies widths and range in this case */
1030     else {
1031         hildon_range_editor_refresh_widths (priv);
1032         hildon_range_editor_apply_current_range (editor);
1033     }
1034
1035     g_object_notify (G_OBJECT (editor), "max");
1036     g_object_thaw_notify (G_OBJECT (editor));
1037 }
1038
1039 gint
1040 hildon_range_editor_get_min                     (HildonRangeEditor *editor)
1041 {
1042     HildonRangeEditorPrivate *priv;
1043     g_return_val_if_fail (HILDON_IS_RANGE_EDITOR (editor), 0);
1044     priv = HILDON_RANGE_EDITOR_GET_PRIVATE (editor);
1045
1046     return priv->range_limits_start;
1047 }
1048
1049 gint
1050 hildon_range_editor_get_max                     (HildonRangeEditor *editor)
1051 {
1052     HildonRangeEditorPrivate *priv;
1053     g_return_val_if_fail (HILDON_IS_RANGE_EDITOR (editor), 0);
1054     priv = HILDON_RANGE_EDITOR_GET_PRIVATE (editor);
1055
1056     return priv->range_limits_end;
1057 }
1058
1059 void
1060 hildon_range_editor_set_separator               (HildonRangeEditor *editor,
1061                                                  const gchar *separator)
1062 {
1063     HildonRangeEditorPrivate *priv;
1064     g_return_if_fail (HILDON_IS_RANGE_EDITOR (editor));
1065     priv = HILDON_RANGE_EDITOR_GET_PRIVATE (editor);
1066
1067     gtk_label_set_text (GTK_LABEL (priv->label), separator);
1068     g_object_notify (G_OBJECT(editor), "separator");
1069 }
1070
1071 const gchar*
1072 hildon_range_editor_get_separator               (HildonRangeEditor *editor)
1073 {
1074     HildonRangeEditorPrivate *priv;
1075     g_return_val_if_fail (HILDON_IS_RANGE_EDITOR (editor), NULL);
1076     priv = HILDON_RANGE_EDITOR_GET_PRIVATE (editor);
1077
1078     return gtk_label_get_text (GTK_LABEL (priv->label));
1079 }