2 * This file is part of hildon-libs
4 * Copyright (C) 2005 Nokia Corporation.
6 * Contact: Luc Pionchon <luc.pionchon@nokia.com>
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.
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.
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
26 * @file hildon-seekbar.c
28 * This file contains the API implementation for Hildon Seekbar widget.
29 * Based on seekbar.gob rev 1.10
40 #include <gtk/gtklabel.h>
41 #include <gtk/gtkframe.h>
42 #include <gtk/gtkalignment.h>
43 #include <gtk/gtkadjustment.h>
44 #include <gtk/gtktoolbar.h>
45 #include <gdk/gdkkeysyms.h>
47 #include "hildon-seekbar.h"
49 #define _(String) dgettext(PACKAGE, String)
51 #define HILDON_SEEKBAR_GET_PRIVATE(obj) \
52 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
53 HILDON_TYPE_SEEKBAR, HildonSeekbarPrivate));
55 typedef struct _HildonSeekbarPrivate HildonSeekbarPrivate;
57 /* our parent class */
58 static GtkScaleClass *parent_class = NULL;
61 static void hildon_seekbar_class_init(HildonSeekbarClass * seekbar_class);
62 static void hildon_seekbar_init(HildonSeekbar * seekbar);
63 static void hildon_seekbar_finalize(GObject * self);
65 /* property functions */
66 static void hildon_seekbar_set_property(GObject * object, guint prop_id,
70 static void hildon_seekbar_get_property(GObject * object, guint prop_id,
74 /* virtual functions */
75 static void hildon_seekbar_size_request(GtkWidget * widget,
76 GtkRequisition * event);
77 static void hildon_seekbar_size_allocate(GtkWidget * widget,
78 GtkAllocation * allocation);
79 static gboolean hildon_seekbar_expose(GtkWidget * widget,
80 GdkEventExpose * event);
81 static gboolean hildon_seekbar_button_press_event(GtkWidget * widget,
82 GdkEventButton * event);
83 static gboolean hildon_seekbar_button_release_event(GtkWidget * widget,
84 GdkEventButton * event);
85 static gboolean hildon_seekbar_keypress(GtkWidget * widget,
92 * These should be named so that they are quite self
95 #define MINIMUM_WIDTH 115
96 #define DEFAULT_HEIGHT 58
97 /* Toolbar width and height defines */
98 #define TOOL_MINIMUM_WIDTH 75
99 #define TOOL_DEFAULT_HEIGHT 40
101 #define DEFAULT_DISPLAYC_BORDER 10
102 #define BUFFER_SIZE 32
103 #define EXTRA_SIDE_BORDER 20
104 #define TOOL_EXTRA_SIDE_BORDER 0
106 /* the number of steps it takes to move from left to right */
107 #define SECONDS_PER_MINUTE 60
110 /* the number of digits precision for the internal range.
111 * note, this needs to be enough so that the step size for
112 * small total_times doesn't get rounded off. Currently set to 3
113 * this is because for the smallest total time ( i.e 1 ) and the current
114 * num steps ( 20 ) is: 1/20 = 0.05. 0.05 is 2 digits, and we
115 * add one for safety */
116 #define MAX_ROUND_DIGITS 3
118 /* enum for properties */
124 /* private variables */
125 struct _HildonSeekbarPrivate {
129 guint fraction; /* This is the amount of time that has progressed from
130 the beginning. It should be an integer between the
131 minimum and maximum values of the corresponding
132 adjustment, ie. adjument->lower and ->upper.. */
136 * Initialises, and returns the type of a hildon seekbar.
138 GType hildon_seekbar_get_type(void)
140 static GType seekbar_type = 0;
143 static const GTypeInfo seekbar_info = {
144 sizeof(HildonSeekbarClass),
145 NULL, /* base_init */
146 NULL, /* base_finalize */
147 (GClassInitFunc) hildon_seekbar_class_init,
148 NULL, /* class_finalize */
149 NULL, /* class_data */
150 sizeof(HildonSeekbar),
152 (GInstanceInitFunc) hildon_seekbar_init,
154 seekbar_type = g_type_register_static(GTK_TYPE_SCALE,
162 * Initialises the seekbar class.
164 static void hildon_seekbar_class_init(HildonSeekbarClass * seekbar_class)
166 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(seekbar_class);
167 GObjectClass *object_class = G_OBJECT_CLASS(seekbar_class);
169 /* set the global parent_class */
170 parent_class = g_type_class_peek_parent(seekbar_class);
172 g_type_class_add_private(seekbar_class, sizeof(HildonSeekbarPrivate));
174 /* setup our widgets v-table */
175 widget_class->size_request = hildon_seekbar_size_request;
176 widget_class->size_allocate = hildon_seekbar_size_allocate;
177 widget_class->expose_event = hildon_seekbar_expose;
178 widget_class->button_press_event = hildon_seekbar_button_press_event;
179 widget_class->button_release_event =
180 hildon_seekbar_button_release_event;
181 widget_class->key_press_event = hildon_seekbar_keypress;
183 /* now the object stuff */
184 object_class->set_property = hildon_seekbar_set_property;
185 object_class->get_property = hildon_seekbar_get_property;
186 object_class->finalize = hildon_seekbar_finalize;
188 /* install the properties */
189 g_object_class_install_property(object_class, PROP_TOTAL_TIME,
190 g_param_spec_double("total_time",
192 "Total playing time of this media file",
194 G_MAXDOUBLE, /* max value */
196 G_PARAM_READABLE | G_PARAM_WRITABLE));
198 g_object_class_install_property(object_class, PROP_POSITION,
199 g_param_spec_double("position",
201 "Current position in this media file",
203 G_MAXDOUBLE, /* max value */
205 G_PARAM_READABLE | G_PARAM_WRITABLE));
206 /* readable and writable */
210 static void hildon_seekbar_init(HildonSeekbar * seekbar)
212 HildonSeekbarPrivate *priv;
213 GtkRange *range = GTK_RANGE(seekbar);
215 priv = HILDON_SEEKBAR_GET_PRIVATE(seekbar);
217 range->orientation = GTK_ORIENTATION_HORIZONTAL;
218 range->flippable = TRUE;
219 range->has_stepper_a = TRUE;
220 range->has_stepper_d = TRUE;
221 range->round_digits = MAX_ROUND_DIGITS;
223 gtk_scale_set_draw_value (GTK_SCALE (seekbar), FALSE);
226 /* is this even necessary? */
227 static void hildon_seekbar_finalize( GObject *obj_self )
230 HildonSeekbarPrivate *priv;
232 self = HILDON_SEEKBAR ( obj_self );
233 priv = HILDON_SEEKBAR_GET_PRIVATE (self);
235 if( G_OBJECT_CLASS( parent_class )->finalize )
236 G_OBJECT_CLASS( parent_class )->finalize( obj_self );
240 /* handle keypress events */
241 static gboolean hildon_seekbar_keypress(GtkWidget * widget,
244 if (event->keyval == GDK_Up || event->keyval == GDK_Down)
246 return ((GTK_WIDGET_CLASS(parent_class)->key_press_event) (widget,
250 /* handle setting of seekbar properties */
252 hildon_seekbar_set_property(GObject * object, guint prop_id,
253 const GValue * value, GParamSpec * pspec)
255 HildonSeekbar *seekbar = HILDON_SEEKBAR(object);
258 case PROP_TOTAL_TIME:
259 hildon_seekbar_set_total_time(seekbar, g_value_get_double(value));
262 hildon_seekbar_set_position(seekbar, g_value_get_double(value));
265 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
270 /* handle getting of seekbar properties */
272 hildon_seekbar_get_property(GObject * object, guint prop_id,
273 GValue * value, GParamSpec * pspec)
275 GtkRange *range = GTK_RANGE(object);
278 case PROP_TOTAL_TIME:
279 g_value_set_double(value, range->adjustment->upper);
282 g_value_set_double(value, range->adjustment->value);
285 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
291 * hildon_seekbar_new:
293 * Creates a new #HildonSeekbar widget.
295 * Return value: A #GtkWidget pointer of #HildonSeekbar widget.
297 GtkWidget *hildon_seekbar_new(void)
299 /* return a new object */
300 return g_object_new(HILDON_TYPE_SEEKBAR, NULL);
304 * hildon_seekbar_get_total_time:
305 * @seekbar: Pointer to #HildonSeekbar widget.
307 * Accessor method for getting total playing time of stream
310 * Returns: Total time as gint.
312 gint hildon_seekbar_get_total_time(HildonSeekbar *seekbar)
315 widget = GTK_WIDGET (seekbar);
316 g_return_val_if_fail(HILDON_IS_SEEKBAR(seekbar), 0);
317 g_return_val_if_fail(GTK_RANGE(widget)->adjustment, 0);
318 return GTK_RANGE(widget)->adjustment->upper;
322 * hildon_seekbar_set_total_time:
323 * @seekbar: Pointer to #HildonSeekbar widget.
324 * @time: Time within range of > 0 && < G_MAXINT
326 * Accessor method for setting total playing time of stream
330 void hildon_seekbar_set_total_time(HildonSeekbar *seekbar, gint time)
334 gboolean value_changed = FALSE;
336 g_return_if_fail(HILDON_IS_SEEKBAR(seekbar));
337 widget = GTK_WIDGET (seekbar);
343 g_return_if_fail(GTK_RANGE(widget)->adjustment);
345 adj = GTK_RANGE(widget)->adjustment;
348 if (adj->value > time) {
350 value_changed = TRUE;
353 adj->step_increment = adj->upper / NUM_STEPS;
354 adj->page_increment = adj->step_increment;
356 gtk_adjustment_changed(adj);
359 gtk_adjustment_value_changed(adj);
360 hildon_seekbar_set_fraction(seekbar,
361 MIN(hildon_seekbar_get_fraction(seekbar),
364 g_object_freeze_notify (G_OBJECT(seekbar));
366 hildon_seekbar_set_position(seekbar,
367 MIN(hildon_seekbar_get_position(seekbar),
370 g_object_notify(G_OBJECT (seekbar), "total-time");
372 g_object_thaw_notify (G_OBJECT(seekbar));
377 * hildon_seekbar_get_fraction:
378 * @seekbar: Pointer to #HildonSeekbar widget.
380 * Accessor method for getting current fraction related to the progress indicator.
381 * It should be between min and max of seekbar range.
383 * Returns: Current fraction.
385 guint hildon_seekbar_get_fraction( HildonSeekbar *seekbar )
387 g_return_val_if_fail( HILDON_IS_SEEKBAR( seekbar ), 0 );
389 return osso_gtk_range_get_stream_position (GTK_RANGE(seekbar));
393 * hildon_seekbar_set_fraction:
394 * @seekbar: Pointer to #HildonSeekbar widget.
395 * @fraction: The new position of the progress indicator.
397 * Method for setting current value related to the progress indicator.
398 * It should be between the minimal and maximal values of the range in seekbar.
400 void hildon_seekbar_set_fraction( HildonSeekbar *seekbar, guint fraction )
402 GtkRange *range = NULL;
403 g_return_if_fail( HILDON_IS_SEEKBAR( seekbar ) );
405 range = GTK_RANGE(GTK_WIDGET(seekbar));
407 g_return_if_fail(fraction <= range->adjustment->upper &&
408 fraction >= range->adjustment->lower);
410 /* Set to show stream indicator. */
411 g_object_set (G_OBJECT (seekbar), "stream_indicator", TRUE, NULL);
413 fraction = CLAMP(fraction, range->adjustment->lower,
414 range->adjustment->upper);
416 osso_gtk_range_set_stream_position( range, fraction );
418 if (fraction < hildon_seekbar_get_position(seekbar))
419 hildon_seekbar_set_position(seekbar, fraction);
423 * hildon_seekbar_get_position:
424 * @seekbar: Pointer to #HildonSeekbar widget.
426 * Accessor method for getting current position in stream
430 * Returns: Current position in stream in seconds.
432 gint hildon_seekbar_get_position(HildonSeekbar *seekbar)
434 g_return_val_if_fail(HILDON_IS_SEEKBAR(seekbar), 0);
435 g_return_val_if_fail(GTK_RANGE(seekbar)->adjustment, 0);
437 return GTK_RANGE(seekbar)->adjustment->value;
441 * hildon_seekbar_set_position:
442 * @seekbar: Pointer to #HildonSeekbar widget.
443 * @time: Time within range of >= 0 && < G_MAXINT
445 * Accessor method for setting current position in stream
448 void hildon_seekbar_set_position(HildonSeekbar *seekbar, gint time)
454 g_return_if_fail(time >= 0);
455 g_return_if_fail(HILDON_IS_SEEKBAR(seekbar));
456 range = GTK_RANGE(seekbar);
457 adj = range->adjustment;
458 g_return_if_fail(adj);
460 /* only change value if it is a different int. this allows us to have
461 smooth scrolls for small total_times */
462 value = floor(adj->value);
464 value = (time < adj->upper) ? time : adj->upper;
465 if (value <= osso_gtk_range_get_stream_position (range)) {
467 gtk_adjustment_value_changed(adj);
469 g_object_notify(G_OBJECT(seekbar), "position");
474 static void hildon_seekbar_size_request(GtkWidget * widget,
475 GtkRequisition * req)
477 HildonSeekbar *self = NULL;
478 HildonSeekbarPrivate *priv = NULL;
479 GtkWidget *parent = NULL;
481 self = HILDON_SEEKBAR(widget);
482 priv = HILDON_SEEKBAR_GET_PRIVATE(self)
485 gtk_widget_get_ancestor(GTK_WIDGET(self), GTK_TYPE_TOOLBAR);
487 priv->is_toolbar = parent ? TRUE : FALSE;
489 if (GTK_WIDGET_CLASS(parent_class)->size_request)
490 GTK_WIDGET_CLASS(parent_class)->size_request(widget, req);
492 req->width = priv->is_toolbar ? TOOL_MINIMUM_WIDTH : MINIMUM_WIDTH;
493 req->height = priv->is_toolbar ? TOOL_DEFAULT_HEIGHT : DEFAULT_HEIGHT;
496 static void hildon_seekbar_size_allocate(GtkWidget * widget,
497 GtkAllocation * allocation)
499 HildonSeekbarPrivate *priv;
501 priv = HILDON_SEEKBAR_GET_PRIVATE(HILDON_SEEKBAR(widget));
503 if (priv->is_toolbar == TRUE)
505 if (allocation->height > TOOL_DEFAULT_HEIGHT) {
507 (allocation->height - TOOL_DEFAULT_HEIGHT) / 2;
508 allocation->height = TOOL_DEFAULT_HEIGHT;
513 if (allocation->height > DEFAULT_HEIGHT) {
515 (allocation->height - DEFAULT_HEIGHT) / 2;
516 allocation->height = DEFAULT_HEIGHT;
520 if (priv->is_toolbar == TRUE)
522 allocation->x += TOOL_EXTRA_SIDE_BORDER;
523 allocation->width -= 2 * TOOL_EXTRA_SIDE_BORDER;
527 allocation->x += EXTRA_SIDE_BORDER;
528 allocation->width -= 2 * EXTRA_SIDE_BORDER;
531 if (GTK_WIDGET_CLASS(parent_class)->size_allocate)
532 GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation);
535 static gboolean hildon_seekbar_expose(GtkWidget * widget,
536 GdkEventExpose * event)
538 HildonSeekbarPrivate *priv;
539 gint extra_side_borders = 0;
541 priv = HILDON_SEEKBAR_GET_PRIVATE(HILDON_SEEKBAR(widget));
543 extra_side_borders = priv->is_toolbar ? TOOL_EXTRA_SIDE_BORDER :
546 if (GTK_WIDGET_DRAWABLE(widget)) {
547 gtk_paint_box(widget->style, widget->window,
548 GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
549 NULL, widget, "seekbar",
550 widget->allocation.x - extra_side_borders,
551 widget->allocation.y,
552 widget->allocation.width + 2 * extra_side_borders,
553 widget->allocation.height);
555 (*GTK_WIDGET_CLASS(parent_class)->expose_event) (widget, event);
562 * Event handler for button_press_event
565 hildon_seekbar_button_press_event(GtkWidget * widget,
566 GdkEventButton * event)
569 HildonSeekbarPrivate *priv;
573 self = HILDON_SEEKBAR(widget);
574 priv = HILDON_SEEKBAR_GET_PRIVATE(self);
576 event->button = event->button == 1 ? 2 : event->button;
578 /* call the parent handler */
579 if (GTK_WIDGET_CLASS(parent_class)->button_press_event)
580 result = GTK_WIDGET_CLASS(parent_class)->button_press_event(widget,
587 * Event handler for button_release_event
590 hildon_seekbar_button_release_event(GtkWidget * widget,
591 GdkEventButton * event)
594 HildonSeekbarPrivate *priv;
595 gboolean result = FALSE;
597 self = HILDON_SEEKBAR(widget);
598 priv = HILDON_SEEKBAR_GET_PRIVATE(self);
600 event->button = event->button == 1 ? 2 : event->button;
602 /* call the parent handler */
603 if (GTK_WIDGET_CLASS(parent_class)->button_release_event)
605 GTK_WIDGET_CLASS(parent_class)->button_release_event(widget,