2009-02-23 Alejandro G. Castro <alex@igalia.com>
[hildon] / src / hildon-volumebar-range.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-volumebar-range
27  * @short_description: This widget is an "workhorse" for #HildonVolumebar
28  * widget. It is not designed to be used as a standalone widget.
29  *
30  * Purpose of this widget is to act as an "container" for GtkScale
31  * widget. #HildonVolumebarRange changes some event parameters so
32  * that #HildonVolumebar can meet its specifications.
33  *
34  * Currently #HildonVolumebarRange models range of [0..100].
35  * 
36  */
37
38 #include                                        <gdk/gdkkeysyms.h>
39
40 #include                                        "hildon-volumebar-range.h"
41
42 #define                                         VOLUMEBAR_RANGE_INITIAL_VALUE 50.0
43
44 #define                                         VOLUMEBAR_RANGE_MINIMUM_VALUE 0.0
45
46 #define                                         VOLUMEBAR_RANGE_MAXIMUM_VALUE 100.0
47
48 #define                                         VOLUMEBAR_RANGE_STEP_INCREMENT_VALUE 5.0
49
50 #define                                         VOLUMEBAR_RANGE_PAGE_INCREMENT_VALUE 5.0
51
52 #define                                         VOLUMEBAR_RANGE_PAGE_SIZE_VALUE 0.0
53
54 #define                                         CHANGE_THRESHOLD 0.001
55
56 static GtkScaleClass*                           parent_class;
57
58 static void 
59 hildon_volumebar_range_class_init               (HildonVolumebarRangeClass*
60                                                  volumerange_class);
61
62 static void 
63 hildon_volumebar_range_init                     (HildonVolumebarRange*
64                                                  volumerange);
65
66 static void 
67 hildon_volumebar_range_set_property             (GObject *object,
68                                                  guint prop_id,
69                                                  const GValue *value,
70                                                  GParamSpec *pspec);
71
72 static void 
73 hildon_volumebar_range_get_property             (GObject *object,
74                                                  guint prop_id,
75                                                  GValue *value,
76                                                  GParamSpec *pspec);
77
78 static gint 
79 hildon_volumebar_range_button_press_event       (GtkWidget *widget,
80                                                  GdkEventButton *event);
81
82 static gint 
83 hildon_volumebar_range_button_release_event     (GtkWidget *widget,
84                                                  GdkEventButton *event);
85
86 static gboolean
87 hildon_volumebar_range_keypress                 (GtkWidget *widget,
88                                                  GdkEventKey *event);
89
90 enum 
91 {
92   PROP_0,
93   PROP_LEVEL
94 };
95
96 /**
97  * hildon_volumebar_range_get_type:
98  *
99  * Initializes and returns the type of a hildon volumebar range.
100  *
101  * Returns: GType of #HildonVolumebarRange
102  */
103 GType G_GNUC_CONST
104 hildon_volumebar_range_get_type                 (void)
105 {
106     static GType volumerange_type = 0;
107
108     if (!volumerange_type) {
109         static const GTypeInfo volumerange_info = {
110             sizeof (HildonVolumebarRangeClass),
111             NULL,       /* base_init */
112             NULL,       /* base_finalize */
113             (GClassInitFunc) hildon_volumebar_range_class_init,
114             NULL,       /* class_finalize */
115             NULL,       /* class_data */
116             sizeof (HildonVolumebarRange),
117             0,  /* n_preallocs */
118             (GInstanceInitFunc) hildon_volumebar_range_init,
119         };
120         volumerange_type = g_type_register_static (GTK_TYPE_SCALE,
121                 "HildonVolumebarRange",
122                 &volumerange_info, 0);
123     }
124
125     return volumerange_type;
126 }
127
128 static void 
129 hildon_volumebar_range_class_init               (HildonVolumebarRangeClass *volumerange_class)
130 {
131     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (volumerange_class);
132     GObjectClass *object_class = G_OBJECT_CLASS (volumerange_class);
133
134     parent_class = g_type_class_peek_parent (volumerange_class);
135
136     widget_class->button_press_event =
137         hildon_volumebar_range_button_press_event;
138     widget_class->button_release_event =
139         hildon_volumebar_range_button_release_event;
140     widget_class->key_press_event = hildon_volumebar_range_keypress;
141
142     object_class->set_property = hildon_volumebar_range_set_property;
143     object_class->get_property = hildon_volumebar_range_get_property; 
144
145     /**
146      * HildonVolumebarRange:level:
147      *
148      * Current volume level. 
149      */
150     g_object_class_install_property (object_class,
151             PROP_LEVEL,
152             g_param_spec_double ("level",
153                 "Level",
154                 "Current volume level",
155                 VOLUMEBAR_RANGE_MINIMUM_VALUE,
156                 VOLUMEBAR_RANGE_MAXIMUM_VALUE,
157                 VOLUMEBAR_RANGE_INITIAL_VALUE,
158                 G_PARAM_READWRITE));
159     return;
160 }
161
162 static void 
163 hildon_volumebar_range_init                     (HildonVolumebarRange *volumerange)
164 {
165   /* do nothing. */
166 }
167
168 static void
169 hildon_volumebar_range_set_property             (GObject *object,
170                                                  guint prop_id,
171                                                  const GValue *value,
172                                                  GParamSpec *pspec)
173 {
174     HildonVolumebarRange *range = HILDON_VOLUMEBAR_RANGE (object);
175
176     switch (prop_id) {
177         case PROP_LEVEL:
178             hildon_volumebar_range_set_level (range, g_value_get_double (value));
179             break;
180
181         default:
182             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
183             break;
184
185     }
186 }
187
188 static void
189 hildon_volumebar_range_get_property             (GObject *object,
190                                                  guint prop_id,
191                                                  GValue *value,
192                                                  GParamSpec *pspec)
193 {
194     HildonVolumebarRange *range = HILDON_VOLUMEBAR_RANGE (object);
195
196     switch (prop_id) {
197
198         case PROP_LEVEL:
199             g_value_set_double (value, hildon_volumebar_range_get_level(range));
200             break;
201
202         default:
203             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
204             break;
205     }
206 }
207
208 static gboolean
209 hildon_volumebar_range_keypress                 (GtkWidget *widget,
210                                                  GdkEventKey *event)
211 {
212     /* Accept arrow keys only if they match the orientation of the widget */
213     if (GTK_RANGE (widget)->orientation == GTK_ORIENTATION_HORIZONTAL)
214     {
215         if (event->keyval == GDK_Up || event->keyval == GDK_Down) {
216             return FALSE;
217         }
218     }
219     else
220     {
221         if (event->keyval == GDK_Left || event->keyval == GDK_Right) {
222             return FALSE;
223         }
224     }
225
226     return ((GTK_WIDGET_CLASS (parent_class)->key_press_event) (widget,
227                 event));
228 }
229
230 GtkWidget*
231 hildon_volumebar_range_new                      (GtkOrientation orientation)
232 {
233     GtkAdjustment * adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (VOLUMEBAR_RANGE_INITIAL_VALUE,
234                 VOLUMEBAR_RANGE_MINIMUM_VALUE,
235                 VOLUMEBAR_RANGE_MAXIMUM_VALUE,
236                 VOLUMEBAR_RANGE_STEP_INCREMENT_VALUE,
237                 VOLUMEBAR_RANGE_PAGE_INCREMENT_VALUE,
238                 VOLUMEBAR_RANGE_PAGE_SIZE_VALUE));
239
240     HildonVolumebarRange *self =
241         g_object_new(HILDON_TYPE_VOLUMEBAR_RANGE,
242                 "adjustment", adjustment,
243                 NULL);
244
245     GTK_RANGE (self)->orientation = orientation;
246
247     /* Default vertical range is upside down for purposes of this widget */
248     gtk_range_set_inverted (GTK_RANGE (self),
249             (orientation == GTK_ORIENTATION_VERTICAL));
250
251     return GTK_WIDGET(self);
252 }
253
254 gdouble 
255 hildon_volumebar_range_get_level                (HildonVolumebarRange *self)
256 {
257     g_return_val_if_fail (HILDON_IS_VOLUMEBAR_RANGE(self), -1.0);
258
259     return gtk_adjustment_get_value (gtk_range_get_adjustment(GTK_RANGE (self)));
260 }
261
262 void 
263 hildon_volumebar_range_set_level                (HildonVolumebarRange * self,
264                                                  gdouble level)
265 {
266     GtkAdjustment *adjustment;
267
268     g_return_if_fail (HILDON_IS_VOLUMEBAR_RANGE (self));
269
270     adjustment = gtk_range_get_adjustment (GTK_RANGE (self));
271
272     /* Check that value has actually changed. Note that it's not safe to
273      * just compare if floats are equivalent or not */
274     if (ABS (gtk_adjustment_get_value (adjustment) - level) > CHANGE_THRESHOLD) {
275         gtk_adjustment_set_value(adjustment, level);
276     }
277 }
278
279 static gint 
280 hildon_volumebar_range_button_press_event       (GtkWidget *widget,
281                                                  GdkEventButton *event)
282 {
283     gboolean result = FALSE;
284
285     /* FIXME: By default, clicking left mouse button on GtkRange moves the
286        slider by one step towards the click location. However, we want stylus
287        taps to move the slider to the position of the tap, which by default
288        is the middle button behaviour. To avoid breaking default GtkRange
289        behaviour, this has been implemented by faking a middle button press. */
290
291     event->button = (event->button == 1) ? 2 : event->button;
292     if (GTK_WIDGET_CLASS (parent_class)->button_press_event) {
293         result = GTK_WIDGET_CLASS (parent_class)->button_press_event(widget, event);
294     }
295
296     return result;
297 }
298
299 static gint
300 hildon_volumebar_range_button_release_event     (GtkWidget *widget,
301                                                  GdkEventButton *event)
302 {
303     gboolean result = FALSE;
304
305     /* FIXME: By default, clicking left mouse button on GtkRange moves the
306        slider by one step towards the click location. However, we want stylus
307        taps to move the slider to the position of the tap, which by default
308        is the middle button behaviour. To avoid breaking default GtkRange
309        behaviour, this has been implemented by faking a middle button press. */
310
311     event->button = event->button == 1 ? 2 : event->button;
312     if (GTK_WIDGET_CLASS (parent_class)->button_release_event) {
313         result = GTK_WIDGET_CLASS(parent_class)->button_release_event(widget, event);
314     }
315
316     return result;
317 }
318