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