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