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