Put function name in the changelog.
[hildon] / src / hildon-controlbar.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-controlbar
27  * @short_description: A widget that allows increasing or decreasing
28  * a value within a pre-defined range.
29  *
30  * #HildonControlbar is a horizontally positioned range widget that is
31  * visually divided into blocks and supports setting a minimum and
32  * maximum value for the range.
33  * 
34  * <example>
35  * <title>HildonControlbar example</title>
36  * <programlisting>
37  * GtkWidget *cbar = hildon_controlbar_new();
38  * hildon_controlbar_set_max (HILDON_CONTROLBAR (cbar), 12);
39  * hildon_controlbar_set_value (HILDON_CONTROLBAR (cbar), 6);
40  * </programlisting>
41  * </example>
42  * 
43  */
44
45 #ifdef                                          HAVE_CONFIG_H
46 #include                                        <config.h>
47 #endif
48
49 #include                                        "hildon-controlbar.h"
50 #include                                        <math.h>
51 #include                                        <gdk/gdk.h>
52 #include                                        <gdk/gdkkeysyms.h>
53 #include                                        <gtk/gtk.h>
54 #include                                        <libintl.h>
55 #include                                        "hildon-controlbar-private.h"
56
57 #define                                         _(string)\
58                                                 dgettext("hildon-libs", string)
59
60 #define                                         DEFAULT_WIDTH 234
61
62 #define                                         DEFAULT_HEIGHT 30
63
64 #define                                         DEFAULT_BORDER_WIDTH 2
65
66 #define                                         HILDON_CONTROLBAR_STEP_INCREMENT 1
67
68 #define                                         HILDON_CONTROLBAR_PAGE_INCREMENT 1
69
70 #define                                         HILDON_CONTROLBAR_PAGE_SIZE 0
71
72 #define                                         HILDON_CONTROLBAR_UPPER_VALUE  10
73
74 #define                                         HILDON_CONTROLBAR_LOWER_VALUE 0.0
75
76 #define                                         HILDON_CONTROLBAR_INITIAL_VALUE 0
77
78 static GtkScaleClass*                           parent_class;
79
80 enum
81 {
82     PROP_0,
83     PROP_MIN = 1,
84     PROP_MAX,
85     PROP_VALUE
86 };
87
88 enum
89 {
90     END_REACHED,
91     LAST_SIGNAL
92 };
93
94 static guint                                    signals[LAST_SIGNAL] = { 0 };
95
96 static void
97 hildon_controlbar_class_init                    (HildonControlbarClass *controlbar_class);
98
99 static void 
100 hildon_controlbar_init                          (HildonControlbar *controlbar);
101
102 static GObject*
103 hildon_controlbar_constructor                   (GType type, 
104                                                  guint n_construct_properties, 
105                                                  GObjectConstructParam *construct_properties);
106
107 static gint 
108 hildon_controlbar_button_press_event            (GtkWidget *widget,
109                                                  GdkEventButton * event);
110
111 static gint 
112 hildon_controlbar_button_release_event          (GtkWidget *widget,
113                                                  GdkEventButton *event);
114
115 static gint
116 hildon_controlbar_expose_event                  (GtkWidget *widget, 
117                                                  GdkEventExpose *event);
118
119 static void
120 hildon_controlbar_size_request                  (GtkWidget *self, 
121                                                  GtkRequisition *req);
122 static void
123 hildon_controlbar_paint                         (HildonControlbar *self, 
124                                                  GdkRectangle * area);
125
126 static gboolean
127 hildon_controlbar_keypress                      (GtkWidget *widget, 
128                                                  GdkEventKey * event);
129
130 static void 
131 hildon_controlbar_set_property                  (GObject *object, 
132                                                  guint param_id,
133                                                  const GValue *value, 
134                                                  GParamSpec *pspec);
135
136 static void 
137 hildon_controlbar_get_property                  (GObject *object, 
138                                                  guint param_id,
139                                                  GValue *value, 
140                                                  GParamSpec *pspec);
141
142 static void
143 hildon_controlbar_value_changed                 (GtkAdjustment *adj, 
144                                                  GtkRange *range);
145
146 static gboolean
147 hildon_controlbar_change_value                  (GtkRange *range, 
148                                                  GtkScrollType scroll,
149                                                  gdouble new_value, 
150                                                  gpointer data);
151
152 /**
153  * hildon_controlbar_get_type:
154  *
155  * Initializes and returns the type of a hildon control bar.
156  *
157  * @Returns: GType of #HildonControlbar
158  */
159 GType G_GNUC_CONST
160 hildon_controlbar_get_type                      (void)
161 {
162     static GType controlbar_type = 0;
163
164     if (!controlbar_type) {
165         static const GTypeInfo controlbar_info = {
166             sizeof (HildonControlbarClass),
167             NULL,       /* base_init */
168             NULL,       /* base_finalize */
169             (GClassInitFunc) hildon_controlbar_class_init,
170             NULL,       /* class_finalize */
171             NULL,       /* class_data */
172             sizeof (HildonControlbar),
173             0,  /* n_preallocs */
174             (GInstanceInitFunc) hildon_controlbar_init,
175         };
176         controlbar_type = g_type_register_static (GTK_TYPE_SCALE,
177                 "HildonControlbar",
178                 &controlbar_info, 0);
179     }
180
181     return controlbar_type;
182 }
183
184 static void
185 hildon_controlbar_class_init                    (HildonControlbarClass *controlbar_class)
186 {
187     GObjectClass *gobject_class = G_OBJECT_CLASS (controlbar_class);
188     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (controlbar_class);
189
190     parent_class = g_type_class_peek_parent(controlbar_class);
191
192     g_type_class_add_private(controlbar_class, sizeof (HildonControlbarPrivate));
193
194     gobject_class->get_property         = hildon_controlbar_get_property;
195     gobject_class->set_property         = hildon_controlbar_set_property;
196     gobject_class->constructor          = hildon_controlbar_constructor;
197     widget_class->size_request          = hildon_controlbar_size_request;
198     widget_class->button_press_event    = hildon_controlbar_button_press_event;
199     widget_class->button_release_event  = hildon_controlbar_button_release_event;
200     widget_class->expose_event          = hildon_controlbar_expose_event;
201     widget_class->key_press_event       = hildon_controlbar_keypress;
202     controlbar_class->end_reached = NULL;
203
204     /**
205      * HildonControlbar:min:
206      *
207      * Controlbar minimum value.
208      */
209     g_object_class_install_property (gobject_class, PROP_MIN,
210             g_param_spec_int ("min",
211                 "Minimum value",
212                 "Smallest possible value",
213                 G_MININT, G_MAXINT,
214                 HILDON_CONTROLBAR_LOWER_VALUE,
215                 G_PARAM_READABLE | G_PARAM_WRITABLE));
216
217     /**
218      * HildonControlbar:max:
219      *
220      * Controlbar maximum value.
221      */
222     g_object_class_install_property (gobject_class, PROP_MAX,
223             g_param_spec_int ("max",
224                 "Maximum value",
225                 "Greatest possible value",
226                 G_MININT, G_MAXINT, 
227                 HILDON_CONTROLBAR_UPPER_VALUE,
228                 G_PARAM_READABLE | G_PARAM_WRITABLE));
229
230     /**
231      * HildonControlbar:value:
232      *
233      * Controlbar current value.
234      */
235     g_object_class_install_property (gobject_class, PROP_VALUE,
236             g_param_spec_int ("value",
237                 "Current value",
238                 "Current value",
239                 G_MININT, G_MAXINT,
240                 HILDON_CONTROLBAR_INITIAL_VALUE,
241                 G_PARAM_READABLE | G_PARAM_WRITABLE) );
242
243
244     gtk_widget_class_install_style_property (widget_class,
245             g_param_spec_uint ("inner_border_width",
246                 "Inner border width",
247                 "The border spacing between the controlbar border and controlbar blocks.",
248                 0, G_MAXINT,
249                 DEFAULT_BORDER_WIDTH,
250                 G_PARAM_READABLE));
251
252     signals[END_REACHED] =
253         g_signal_new("end-reached",
254                 G_OBJECT_CLASS_TYPE (gobject_class),
255                 G_SIGNAL_RUN_FIRST,
256                 G_STRUCT_OFFSET (HildonControlbarClass, end_reached),
257                 NULL, NULL,
258                 g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1,
259                 G_TYPE_BOOLEAN);
260 }
261
262 static void 
263 hildon_controlbar_init                          (HildonControlbar *controlbar)
264 {
265     GtkRange *range;
266     HildonControlbarPrivate *priv;
267
268     /* Initialize the private property */
269     priv = HILDON_CONTROLBAR_GET_PRIVATE(controlbar);
270     g_assert (priv);
271
272     priv->button_press = FALSE;
273     priv->old_value = 0;
274     range = GTK_RANGE (controlbar);
275
276     range->has_stepper_a = TRUE;
277     range->has_stepper_d = TRUE;
278     range->round_digits = -1;
279
280     gtk_widget_set_size_request (GTK_WIDGET (controlbar), 
281             DEFAULT_WIDTH,
282             DEFAULT_HEIGHT);
283
284     g_signal_connect (range, "change-value",
285             G_CALLBACK (hildon_controlbar_change_value), NULL);
286 }
287
288 static GObject*
289 hildon_controlbar_constructor                   (GType type, 
290                                                  guint n_construct_properties, 
291                                                  GObjectConstructParam *construct_properties)
292 {
293     GObject *obj;
294     GtkAdjustment *adj;  
295
296     obj = G_OBJECT_CLASS (parent_class)->constructor (type, 
297             n_construct_properties, construct_properties);
298
299     gtk_scale_set_draw_value (GTK_SCALE (obj), FALSE);
300
301     /* Initialize the GtkAdjustment of the controlbar*/
302     adj = GTK_RANGE (obj)->adjustment;
303     adj->step_increment = HILDON_CONTROLBAR_STEP_INCREMENT;
304     adj->page_increment = HILDON_CONTROLBAR_PAGE_INCREMENT;
305     adj->page_size = HILDON_CONTROLBAR_PAGE_SIZE;
306
307     g_signal_connect (adj, "value-changed", 
308             G_CALLBACK (hildon_controlbar_value_changed), obj);
309     return obj;
310 }
311
312 static void 
313 hildon_controlbar_set_property                  (GObject *object, 
314                                                  guint param_id,
315                                                  const GValue *value, 
316                                                  GParamSpec *pspec)
317 {
318     HildonControlbar *controlbar = HILDON_CONTROLBAR (object);
319
320     switch (param_id)
321     {
322         case PROP_MIN:
323             hildon_controlbar_set_min (controlbar, g_value_get_int(value));
324             break;
325
326         case PROP_MAX:
327             hildon_controlbar_set_max (controlbar, g_value_get_int(value));
328             break;
329
330         case PROP_VALUE:
331             hildon_controlbar_set_value (controlbar, g_value_get_int(value));
332             break;
333
334         default:
335             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
336             break;
337     }
338 }
339
340 static void hildon_controlbar_get_property      (GObject *object, 
341                                                  guint param_id,
342                                                  GValue *value, 
343                                                  GParamSpec *pspec)
344 {
345     HildonControlbar *controlbar = HILDON_CONTROLBAR(object);
346
347     switch (param_id)
348     {
349         case PROP_MIN:
350             g_value_set_int (value, hildon_controlbar_get_min (controlbar));
351             break;
352
353         case PROP_MAX:
354             g_value_set_int (value, hildon_controlbar_get_max (controlbar));
355             break;
356
357         case PROP_VALUE:
358             g_value_set_int (value, hildon_controlbar_get_value (controlbar));
359             break;
360
361         default:
362             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
363             break;
364     }
365 }
366
367
368 static void
369 hildon_controlbar_value_changed                 (GtkAdjustment *adj, 
370                                                  GtkRange *range)
371 {
372     HildonControlbarPrivate *priv = HILDON_CONTROLBAR_GET_PRIVATE(range);
373     g_assert (priv);
374
375     /* Change the controlbar value if the adjusted value is large enough 
376      * otherwise, keep the old value
377      */
378     if (ABS(ceil (adj->value) - priv->old_value) >= 1)
379     {
380         priv->old_value = ceil (adj->value);
381         adj->value = priv->old_value;
382     }
383     else
384         g_signal_stop_emission_by_name (adj, "value-changed");
385
386     gtk_adjustment_set_value (adj, priv->old_value);
387 }
388
389 /**
390  * hildon_controlbar_new:
391  * 
392  * Creates a new #HildonControlbar widget.
393  *
394  * Returns: a #GtkWidget pointer of newly created control bar
395  * widget
396  */
397 GtkWidget*
398 hildon_controlbar_new                           (void)
399 {
400     return GTK_WIDGET (g_object_new (HILDON_TYPE_CONTROLBAR, NULL));
401 }
402
403 /* This function prevents Up and Down keys from changing the
404  * widget's value (like Left and Right).
405  * Instead they are used for changing focus to other widgtes.
406  */
407 static gboolean
408 hildon_controlbar_keypress                      (GtkWidget *widget, 
409                                                  GdkEventKey *event)
410 {
411     if (event->keyval == GDK_Up || event->keyval == GDK_Down)
412         return FALSE;
413
414     return ((GTK_WIDGET_CLASS (parent_class)->key_press_event) (widget, event));
415 }
416
417 static void
418 hildon_controlbar_size_request                  (GtkWidget *self, 
419                                                  GtkRequisition *req)
420 {
421     if (GTK_WIDGET_CLASS (parent_class)->size_request)
422         GTK_WIDGET_CLASS (parent_class)->size_request(self, req);
423
424     req->width = DEFAULT_WIDTH;
425     req->height = DEFAULT_HEIGHT;
426 }
427
428 /**
429  * hildon_controlbar_set_value:
430  * @self: pointer to #HildonControlbar
431  * @value: value in range of >= 0 && < G_MAX_INT
432  *
433  * Set the current value of the control bar to the specified value.
434  */
435 void 
436 hildon_controlbar_set_value                     (HildonControlbar * self, 
437                                                  gint value)
438 {
439     GtkAdjustment *adj;
440     g_return_if_fail (HILDON_IS_CONTROLBAR (self));
441     adj = GTK_RANGE (self)->adjustment;
442
443     g_return_if_fail (value >= 0);
444
445     if (value >= adj->upper)
446         value = adj->upper;
447     else if (value <= adj->lower)
448         value = adj->lower;
449
450     adj->value = value;
451     gtk_adjustment_value_changed (adj);
452
453     g_object_notify (G_OBJECT(self), "value");
454 }
455
456 /**
457  * hildon_controlbar_get_value:
458  * @self: pointer to #HildonControlbar
459  *
460  * Returns: current value as gint
461  */
462 gint
463 hildon_controlbar_get_value                     (HildonControlbar * self)
464 {
465     GtkAdjustment *adj;
466     g_return_val_if_fail (HILDON_IS_CONTROLBAR (self), 0);
467     adj = GTK_RANGE(self)->adjustment;
468
469     return (gint) ceil(adj->value);
470 }
471
472 /**
473  * hildon_controlbar_set_max:
474  * @self: pointer to #HildonControlbar
475  * @max: maximum value to set. The value needs to be greater than 0.
476  *
477  * Set the control bar's maximum to the given value.
478  * 
479  * If the new maximum is smaller than current value, the value will be
480  * adjusted so that it equals the new maximum.
481  */
482 void 
483 hildon_controlbar_set_max                       (HildonControlbar * self, 
484                                                  gint max)
485 {
486     GtkAdjustment *adj;
487     g_return_if_fail (HILDON_IS_CONTROLBAR (self));
488     adj = GTK_RANGE (self)->adjustment;
489
490     if (max < adj->lower)
491         max = adj->lower;
492
493     if (adj->value > max)
494         hildon_controlbar_set_value (self, max);
495
496     adj->upper = max;
497     gtk_adjustment_changed (adj);
498
499     g_object_notify (G_OBJECT(self), "max");
500 }
501
502 /**
503  * hildon_controlbar_set_min:
504  * @self: pointer to #HildonControlbar
505  * @min: minimum value to set. The value needs to be greater than or
506  * equal to 0.
507  *
508  * Set the control bar's minimum to the given value.
509  *
510  * If the new minimum is smaller than current value, the value will be
511  * adjusted so that it equals the new minimum.
512  */
513 void
514 hildon_controlbar_set_min                       (HildonControlbar *self, 
515                                                  gint min)
516 {
517     GtkAdjustment *adj;
518     g_return_if_fail (HILDON_IS_CONTROLBAR (self));
519     adj = GTK_RANGE (self)->adjustment;
520
521     if (min > adj->upper)
522         min = adj->upper;
523
524     if (adj->value < min)
525         hildon_controlbar_set_value (self, min);
526
527     adj->lower = min;
528     gtk_adjustment_changed (adj);
529     g_object_notify (G_OBJECT(self), "min");
530 }
531
532 /**
533  * hildon_controlbar_set_range:
534  * @self: pointer to #HildonControlbar
535  * @max: maximum value to set. The value needs to be greater than 0.
536  * @min: Minimum value to set. The value needs to be greater than or
537  * equal to 0.
538  *
539  * Set the controlbars range to the given value
540  * 
541  * If the new maximum is smaller than current value, the value will be
542  * adjusted so that it equals the new maximum.
543  *
544  * If the new minimum is smaller than current value, the value will be
545  * adjusted so that it equals the new minimum.
546  */
547 void 
548 hildon_controlbar_set_range                     (HildonControlbar *self, 
549                                                  gint min,
550                                                  gint max)
551 {
552     g_return_if_fail (HILDON_IS_CONTROLBAR (self));
553
554     if (min > max)
555         min = max;
556
557     /* We need to set max first here, because when min is set before
558      * max is set, it would end up 0, because max can't be bigger than 0.
559      */
560     hildon_controlbar_set_max (self, max);
561     hildon_controlbar_set_min (self, min);
562 }
563
564 /**
565  * hildon_controlbar_get_max:
566  * @self: a pointer to #HildonControlbar
567  *
568  * Returns: maximum value of control bar
569  */
570 gint hildon_controlbar_get_max                  (HildonControlbar *self)
571 {
572     GtkAdjustment *adj;
573     g_return_val_if_fail (HILDON_IS_CONTROLBAR (self), 0);
574     adj = GTK_RANGE (self)->adjustment;
575
576     return (gint) adj->upper;
577 }
578
579 /**
580  * hildon_controlbar_get_min:
581  * @self: a pointer to #HildonControlbar
582  *
583  * Returns: minimum value of controlbar
584  */
585 gint 
586 hildon_controlbar_get_min                       (HildonControlbar *self)
587 {
588     GtkAdjustment *adj = GTK_RANGE (self)->adjustment;
589     return (gint) adj->lower;
590 }
591
592 /*
593  * Event handler for button press
594  * Need to change button1 to button2 before passing this event to
595  * parent handler. (see specs)
596  * Also updates button_press variable so that we can draw highlights
597  * correctly
598  */
599 static gint 
600 hildon_controlbar_button_press_event            (GtkWidget *widget,
601                                                  GdkEventButton *event)
602 {
603     HildonControlbar *self;
604     HildonControlbarPrivate *priv;
605     gboolean result = FALSE;
606
607     g_return_val_if_fail (widget, FALSE);
608     g_return_val_if_fail (event, FALSE);
609
610     self = HILDON_CONTROLBAR (widget);
611     priv = HILDON_CONTROLBAR_GET_PRIVATE (self);
612     g_assert (priv);
613
614     priv->button_press = TRUE;
615     event->button = event->button == 1 ? 2 : event->button;
616
617     /* Ugh dirty hack. We manipulate the mouse event location to
618        compensate for centering the widget in case it is taller than the
619        default height. */
620     if (widget->allocation.height > DEFAULT_HEIGHT) {
621         gint difference = widget->allocation.height - DEFAULT_HEIGHT;
622
623         if (difference & 1)
624             difference += 1;
625         difference = difference / 2;
626
627         event->y -= difference;
628     }
629
630
631     /* call the parent handler */
632     if (GTK_WIDGET_CLASS (parent_class)->button_press_event)
633         result = GTK_WIDGET_CLASS (parent_class)->button_press_event(widget, event);
634
635     return result;
636 }
637
638 /*
639  * Purpose of this function is to prevent Up and Down keys from 
640  * changing the widget's value (like Left and Right). Instead they 
641  * are used for changing focus to other widgtes.
642  */
643 static gboolean
644 hildon_controlbar_change_value                  (GtkRange *range,
645                                                  GtkScrollType scroll,
646                                                  gdouble new_value,
647                                                  gpointer data)
648 {
649     HildonControlbarPrivate *priv;
650     GtkAdjustment *adj = range->adjustment;
651     gdouble vv = adj->upper - adj->lower;
652     gint calc = ((new_value - adj->lower) / vv) * (vv + 1.0) + adj->lower;
653
654     priv = HILDON_CONTROLBAR_GET_PRIVATE(range);
655     g_assert (priv);
656
657     /* Emit a signal when upper or lower limit is reached */
658     switch (scroll)
659     {
660         case GTK_SCROLL_STEP_FORWARD :
661         case GTK_SCROLL_PAGE_FORWARD :
662             if( adj->value == priv->old_value )
663                 if( adj->value == adj->upper )
664                     g_signal_emit( G_OBJECT(range), signals[END_REACHED], 0, TRUE );
665             break;
666
667         case GTK_SCROLL_STEP_BACKWARD :
668         case GTK_SCROLL_PAGE_BACKWARD :
669             if( adj->value == priv->old_value )
670                 if( adj->value == adj->lower )
671                     g_signal_emit( G_OBJECT(range), signals[END_REACHED], 0, FALSE );
672             break;
673
674         default:
675             break;
676     }
677
678     GTK_RANGE_CLASS (parent_class)->change_value (range, scroll, calc);
679
680     return TRUE;
681 }
682
683 /*
684  * Event handler for button release
685  * Need to change button1 to button2 before passing this event to
686  * parent handler. (see specs)
687  * Also updates button_press variable so that we can draw hilites
688  * correctly
689  */
690 static gint 
691 hildon_controlbar_button_release_event          (GtkWidget *widget,
692                                                  GdkEventButton *event)
693 {
694     HildonControlbar *self;
695     HildonControlbarPrivate *priv;
696     gboolean result = FALSE;
697
698     g_return_val_if_fail (widget, FALSE);
699     g_return_val_if_fail (event, FALSE);
700
701     self = HILDON_CONTROLBAR (widget);
702     priv = HILDON_CONTROLBAR_GET_PRIVATE (self);
703     g_assert (priv);
704
705     priv->button_press = FALSE;
706     event->button = event->button == 1 ? 2 : event->button;
707
708     /* call the parent handler */
709     if (GTK_WIDGET_CLASS (parent_class)->button_release_event)
710         result = GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
711
712     return result;
713 }
714
715 /*
716  * Event handler for expose event
717  */
718 static gint 
719 hildon_controlbar_expose_event                  (GtkWidget *widget,
720                                                  GdkEventExpose * event)
721 {
722     HildonControlbar *self = NULL;
723
724     gboolean result = FALSE;
725     gint old_height = -1;
726     gint old_y = -1;
727
728     g_return_val_if_fail (event, FALSE);
729     g_return_val_if_fail (HILDON_IS_CONTROLBAR(widget), FALSE);
730
731     self = HILDON_CONTROLBAR(widget);
732
733     old_height = widget->allocation.height;
734     old_y = widget->allocation.y;
735
736     if (widget->allocation.height > DEFAULT_HEIGHT) {
737         int difference = widget->allocation.height - DEFAULT_HEIGHT;
738
739         if (difference & 1)
740             difference += 1;
741
742         difference = difference / 2;
743
744         widget->allocation.y += difference;
745         widget->allocation.height = DEFAULT_HEIGHT;
746     }
747
748     /* call the parent handler */
749     if (GTK_WIDGET_CLASS (parent_class)->expose_event)
750         result = GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
751
752     hildon_controlbar_paint (self, &event->area);
753
754     widget->allocation.height = old_height;
755     widget->allocation.y = old_y;
756
757     return TRUE;
758 }
759
760 /*
761  * Paint method.
762  * This is where all the work is actually done...
763  */
764 static void
765 hildon_controlbar_paint                         (HildonControlbar *self,
766                                                  GdkRectangle *area)
767 {
768     HildonControlbarPrivate *priv;
769     GtkWidget *widget = GTK_WIDGET(self);
770     GtkAdjustment *ctrlbar = GTK_RANGE(self)->adjustment;
771     gint x = widget->allocation.x;
772     gint y = widget->allocation.y;
773     gint h = widget->allocation.height;
774     gint w = widget->allocation.width;
775     gint max = 0;
776     gint stepper_size = 0;
777     gint stepper_spacing = 0;
778     gint inner_border_width = 0;
779     gint block_area = 0, block_width = 0, block_x = 0, block_max = 0, block_height,block_y;
780     /* Number of blocks on the controlbar */
781     guint block_count = 0;
782     /* Number of displayed active blocks */
783     guint block_act = 0;
784     /* Minimum no. of blocks visible */
785     guint block_min = 0;
786     gint separatingpixels = 2;
787     gint block_remains = 0;
788     gint i, start_x, end_x, current_width;
789     GtkStateType state = GTK_STATE_NORMAL;
790
791     g_return_if_fail(area);
792
793     priv = HILDON_CONTROLBAR_GET_PRIVATE(self);
794     g_assert (priv);
795
796     if (GTK_WIDGET_SENSITIVE (self) == FALSE)
797         state = GTK_STATE_INSENSITIVE;
798
799     gtk_widget_style_get (GTK_WIDGET (self),
800             "stepper-size", &stepper_size,
801             "stepper-spacing", &stepper_spacing,
802             "inner_border_width", &inner_border_width, NULL);
803
804     block_area = (w - 2 * stepper_size - 2 * stepper_spacing - 2 * inner_border_width);
805
806     if (block_area <= 0)
807         return;
808
809     block_min = 1;
810     block_max = ctrlbar->upper - ctrlbar->lower + block_min; 
811     block_act = priv->old_value - GTK_RANGE (self)->adjustment->lower + block_min;
812
813     /* We check border width and maximum value and adjust
814      * separating pixels for block width here. If the block size would
815      * become too small, we make the separators smaller. Graceful fallback.
816      */
817     max = ctrlbar->upper;
818     if(ctrlbar->upper == 0)
819         separatingpixels = 3;
820     else if ((block_area - ((max - 1) * 3)) / max >= 4) 
821         separatingpixels = 3;
822     else if ((block_area - ((max - 1) * 2)) / max >= 4) 
823         separatingpixels = 2;
824     else if ((block_area - ((max - 1) * 1)) / max >= 4)
825         separatingpixels = 1;
826     else
827         separatingpixels = 0;
828
829     if (block_max == 0)
830     {
831         /* If block max is 0 then we dim the whole control. */
832         state = GTK_STATE_INSENSITIVE;
833         block_width = block_area;
834         block_remains = 0;
835         block_max = 1;
836     }
837     else
838     {
839         block_width =
840             (block_area - (separatingpixels * (block_max - 1))) / block_max;
841         block_remains =
842             (block_area - (separatingpixels * (block_max - 1))) % block_max;
843     }
844
845     block_x = x + stepper_size + stepper_spacing + inner_border_width;
846     block_y = y + inner_border_width;
847     block_height = h - 2 * inner_border_width;
848
849     block_count = ctrlbar->value - ctrlbar->lower +  block_min;
850
851     if (block_count == 0)
852             block_count = 1;
853     /* Without this there is vertical block corruption when block_height = 
854        1. This should work from 0 up to whatever */
855
856     if (block_height < 2)
857         block_height = 2;
858
859     /* 
860      * Changed the drawing of the blocks completely,
861      * because of "do-not-resize-when-changing-max"-specs.
862      * Now the code calculates from the block_remains when
863      * it should add one pixel to the block and when not.
864      */
865
866     for (i = 1; i <= block_max; i++) {
867
868         /* Here we calculate whether we add one pixel to current_width or
869            not. */
870         start_x = block_width * (i - 1) + ((i - 1) * block_remains) / block_max;
871         end_x = block_width * i + (i * block_remains) / block_max;
872         current_width = end_x - start_x;
873
874         gtk_paint_box (widget->style, widget->window, state,
875                 (i <= block_count) ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
876                 NULL, widget, "hildon_block",
877                 block_x, block_y, current_width,
878                 block_height);
879
880         /* We keep the block_x separate because of the
881            'separatingpixels' */
882         block_x += current_width + separatingpixels;
883     }
884
885 }