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