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