* hildon-widgets/hildon-calendar-popup.c * hildon-widgets/hildon-color-button.c ...
[hildon] / hildon-widgets / hildon-volumebar-range.c
1 /*
2  * This file is part of hildon-libs
3  *
4  * Copyright (C) 2005 Nokia Corporation.
5  *
6  * Contact: Luc Pionchon <luc.pionchon@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; either 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  * @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   return;
147 }
148
149 static void
150 hildon_volumebar_range_set_property(GObject * object,
151                                     guint prop_id,
152                                     const GValue * value,
153                                     GParamSpec * pspec)
154 {
155     HildonVolumebarRange *range = HILDON_VOLUMEBAR_RANGE(object);
156
157     switch (prop_id) {
158     case PROP_LEVEL:
159         hildon_volumebar_range_set_level(range, g_value_get_double(value));
160         break;
161     default:
162         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
163         break;
164     }
165 }
166
167 static void
168 hildon_volumebar_range_get_property(GObject * object,
169                                     guint prop_id,
170                                     GValue * value,
171                                     GParamSpec * pspec)
172 {
173     HildonVolumebarRange *range = HILDON_VOLUMEBAR_RANGE(object);
174
175     switch (prop_id) {
176     case PROP_LEVEL:
177         g_value_set_double(value, hildon_volumebar_range_get_level(range));
178         break;
179     default:
180         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
181         break;
182     }
183 }
184
185 static
186 gboolean hildon_volumebar_range_keypress(GtkWidget * widget,
187                                          GdkEventKey * event)
188 {
189     /* Accept arrow keys only if they match the orientation of the widget */
190     if (GTK_RANGE (widget)->orientation == GTK_ORIENTATION_HORIZONTAL)
191       {
192         if (event->keyval == GDK_Up || event->keyval == GDK_Down) {
193           return FALSE;
194         }
195       }
196     else
197       {
198         if (event->keyval == GDK_Left || event->keyval == GDK_Right) {
199           return FALSE;
200         }
201       }
202
203     return ((GTK_WIDGET_CLASS(parent_class)->key_press_event) (widget,
204                                                                event));
205 }
206
207 GtkWidget *
208 hildon_volumebar_range_new(GtkOrientation orientation)
209 {
210     GtkAdjustment * adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (VOLUMEBAR_RANGE_INITIAL_VALUE,
211                                               VOLUMEBAR_RANGE_MINIMUM_VALUE,
212                                               VOLUMEBAR_RANGE_MAXIMUM_VALUE,
213                                               VOLUMEBAR_RANGE_STEP_INCREMENT_VALUE,
214                                               VOLUMEBAR_RANGE_PAGE_INCREMENT_VALUE,
215                                               VOLUMEBAR_RANGE_PAGE_SIZE_VALUE));
216     HildonVolumebarRange *self =
217         g_object_new(HILDON_VOLUMEBAR_RANGE_TYPE,
218                      "adjustment", adjustment,
219                      NULL);
220
221     GTK_RANGE(self)->orientation = orientation;
222
223     /* Default vertical range is upside down for purposes of this widget */
224     gtk_range_set_inverted(GTK_RANGE(self),
225                            (orientation == GTK_ORIENTATION_VERTICAL));
226
227     return GTK_WIDGET(self);
228 }
229
230 gdouble 
231 hildon_volumebar_range_get_level(HildonVolumebarRange * self)
232 {
233     g_return_val_if_fail(self, -1.0);
234     return GTK_RANGE (self)->adjustment->value;
235 }
236
237 void 
238 hildon_volumebar_range_set_level(HildonVolumebarRange * self,
239                                  gdouble level)
240 {
241     gdouble newlevel;
242     g_return_if_fail(self);
243    
244     /* Although the range can clamp by itself, we do the clamping
245      * here to prevent sending value-changed signal when someone
246      * unsuccessfully tries to set level to illegal value. */
247     newlevel = CLAMP (level, GTK_RANGE (self)->adjustment->lower,
248                       GTK_RANGE (self)->adjustment->upper);
249
250     /* Check that value is actually changed. Note that it's not safe to
251      * just compare if floats are equivalent or not */
252     if (ABS(GTK_RANGE (self)->adjustment->value - newlevel) > CHANGE_THRESHOLD) {
253         /* This might be a bit faster because this skips
254          * gtkadjustment's own clamping and check if value has
255          * indeed changed. */
256         GTK_RANGE (self)->adjustment->value = newlevel;
257         gtk_adjustment_value_changed(GTK_RANGE (self)->adjustment);
258     }
259 }
260
261 static gint 
262 hildon_volumebar_range_button_press_event(GtkWidget * widget,
263                                           GdkEventButton *
264                                           event)
265 {
266     gboolean result = FALSE;
267
268     /* FIXME: Ugly hack to trick GtkRange event handler */
269     event->button = (event->button == 1) ? 2 : event->button;
270     if (GTK_WIDGET_CLASS(parent_class)->button_press_event) {
271         result =
272             GTK_WIDGET_CLASS(parent_class)->button_press_event(widget,
273                                                                event);
274     }
275     return result;
276 }
277
278 static gint 
279 hildon_volumebar_range_button_release_event(GtkWidget * widget,
280                                             GdkEventButton *
281                                             event)
282 {
283     gboolean result = FALSE;
284
285     /* FIXME: Ugly hack to trick GtkRange event handler */
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 }