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