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