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