No issues found
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3 * st-adjustment.c: Adjustment object
4 *
5 * Copyright 2008 OpenedHand
6 * Copyright 2009 Intel Corporation.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU Lesser General Public License,
10 * version 2.1, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /**
22 * SECTION:st-adjustment
23 * @short_description: A GObject representing an adjustable bounded value
24 *
25 * The #StAdjustment object represents a range of values bounded between a
26 * minimum and maximum, together with step and page increments and a page size.
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <glib-object.h>
34 #include <clutter/clutter.h>
35
36 #include "st-adjustment.h"
37 #include "st-private.h"
38
39 G_DEFINE_TYPE (StAdjustment, st_adjustment, G_TYPE_OBJECT)
40
41 #define ADJUSTMENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_ADJUSTMENT, StAdjustmentPrivate))
42
43 struct _StAdjustmentPrivate
44 {
45 /* Do not sanity-check values while constructing,
46 * not all properties may be set yet. */
47 gboolean is_constructing : 1;
48
49 gdouble lower;
50 gdouble upper;
51 gdouble value;
52 gdouble step_increment;
53 gdouble page_increment;
54 gdouble page_size;
55 };
56
57 enum
58 {
59 PROP_0,
60
61 PROP_LOWER,
62 PROP_UPPER,
63 PROP_VALUE,
64 PROP_STEP_INC,
65 PROP_PAGE_INC,
66 PROP_PAGE_SIZE,
67 };
68
69 enum
70 {
71 CHANGED,
72
73 LAST_SIGNAL
74 };
75
76 static guint signals[LAST_SIGNAL] = { 0, };
77
78 static gboolean st_adjustment_set_lower (StAdjustment *adjustment,
79 gdouble lower);
80 static gboolean st_adjustment_set_upper (StAdjustment *adjustment,
81 gdouble upper);
82 static gboolean st_adjustment_set_step_increment (StAdjustment *adjustment,
83 gdouble step);
84 static gboolean st_adjustment_set_page_increment (StAdjustment *adjustment,
85 gdouble page);
86 static gboolean st_adjustment_set_page_size (StAdjustment *adjustment,
87 gdouble size);
88
89 static void
90 st_adjustment_constructed (GObject *object)
91 {
92 GObjectClass *g_class;
93 StAdjustment *self = ST_ADJUSTMENT (object);
94
95 g_class = G_OBJECT_CLASS (st_adjustment_parent_class);
96 /* The docs say we're suppose to chain up, but would crash without
97 * some extra care. */
98 if (g_class && g_class->constructed &&
99 g_class->constructed != st_adjustment_constructed)
100 {
101 g_class->constructed (object);
102 }
103
104 ST_ADJUSTMENT (self)->priv->is_constructing = FALSE;
105 st_adjustment_clamp_page (self, self->priv->lower, self->priv->upper);
106 }
107
108 static void
109 st_adjustment_get_property (GObject *gobject,
110 guint prop_id,
111 GValue *value,
112 GParamSpec *pspec)
113 {
114 StAdjustmentPrivate *priv = ST_ADJUSTMENT (gobject)->priv;
115
116 switch (prop_id)
117 {
118 case PROP_LOWER:
119 g_value_set_double (value, priv->lower);
120 break;
121
122 case PROP_UPPER:
123 g_value_set_double (value, priv->upper);
124 break;
125
126 case PROP_VALUE:
127 g_value_set_double (value, priv->value);
128 break;
129
130 case PROP_STEP_INC:
131 g_value_set_double (value, priv->step_increment);
132 break;
133
134 case PROP_PAGE_INC:
135 g_value_set_double (value, priv->page_increment);
136 break;
137
138 case PROP_PAGE_SIZE:
139 g_value_set_double (value, priv->page_size);
140 break;
141
142 default:
143 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
144 break;
145 }
146 }
147
148 static void
149 st_adjustment_set_property (GObject *gobject,
150 guint prop_id,
151 const GValue *value,
152 GParamSpec *pspec)
153 {
154 StAdjustment *adj = ST_ADJUSTMENT (gobject);
155
156 switch (prop_id)
157 {
158 case PROP_LOWER:
159 st_adjustment_set_lower (adj, g_value_get_double (value));
160 break;
161
162 case PROP_UPPER:
163 st_adjustment_set_upper (adj, g_value_get_double (value));
164 break;
165
166 case PROP_VALUE:
167 st_adjustment_set_value (adj, g_value_get_double (value));
168 break;
169
170 case PROP_STEP_INC:
171 st_adjustment_set_step_increment (adj, g_value_get_double (value));
172 break;
173
174 case PROP_PAGE_INC:
175 st_adjustment_set_page_increment (adj, g_value_get_double (value));
176 break;
177
178 case PROP_PAGE_SIZE:
179 st_adjustment_set_page_size (adj, g_value_get_double (value));
180 break;
181
182 default:
183 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
184 break;
185 }
186 }
187
188 static void
189 st_adjustment_class_init (StAdjustmentClass *klass)
190 {
191 GObjectClass *object_class = G_OBJECT_CLASS (klass);
192
193 g_type_class_add_private (klass, sizeof (StAdjustmentPrivate));
194
195 object_class->constructed = st_adjustment_constructed;
196 object_class->get_property = st_adjustment_get_property;
197 object_class->set_property = st_adjustment_set_property;
198
199 g_object_class_install_property (object_class,
200 PROP_LOWER,
201 g_param_spec_double ("lower",
202 "Lower",
203 "Lower bound",
204 -G_MAXDOUBLE,
205 G_MAXDOUBLE,
206 0.0,
207 ST_PARAM_READWRITE |
208 G_PARAM_CONSTRUCT));
209 g_object_class_install_property (object_class,
210 PROP_UPPER,
211 g_param_spec_double ("upper",
212 "Upper",
213 "Upper bound",
214 -G_MAXDOUBLE,
215 G_MAXDOUBLE,
216 0.0,
217 ST_PARAM_READWRITE |
218 G_PARAM_CONSTRUCT));
219 g_object_class_install_property (object_class,
220 PROP_VALUE,
221 g_param_spec_double ("value",
222 "Value",
223 "Current value",
224 -G_MAXDOUBLE,
225 G_MAXDOUBLE,
226 0.0,
227 ST_PARAM_READWRITE |
228 G_PARAM_CONSTRUCT));
229 g_object_class_install_property (object_class,
230 PROP_STEP_INC,
231 g_param_spec_double ("step-increment",
232 "Step Increment",
233 "Step increment",
234 0.0,
235 G_MAXDOUBLE,
236 0.0,
237 ST_PARAM_READWRITE |
238 G_PARAM_CONSTRUCT));
239 g_object_class_install_property (object_class,
240 PROP_PAGE_INC,
241 g_param_spec_double ("page-increment",
242 "Page Increment",
243 "Page increment",
244 0.0,
245 G_MAXDOUBLE,
246 0.0,
247 ST_PARAM_READWRITE |
248 G_PARAM_CONSTRUCT));
249 g_object_class_install_property (object_class,
250 PROP_PAGE_SIZE,
251 g_param_spec_double ("page-size",
252 "Page Size",
253 "Page size",
254 0.0,
255 G_MAXDOUBLE,
256 0.0,
257 ST_PARAM_READWRITE |
258 G_PARAM_CONSTRUCT));
259 /**
260 * StAdjustment::changed:
261 *
262 * Emitted when any of the adjustment values have changed
263 */
264 signals[CHANGED] =
265 g_signal_new ("changed",
266 G_TYPE_FROM_CLASS (klass),
267 G_SIGNAL_RUN_LAST,
268 G_STRUCT_OFFSET (StAdjustmentClass, changed),
269 NULL, NULL, NULL,
270 G_TYPE_NONE, 0);
271 }
272
273 static void
274 st_adjustment_init (StAdjustment *self)
275 {
276 self->priv = ADJUSTMENT_PRIVATE (self);
277
278 self->priv->is_constructing = TRUE;
279 }
280
281 StAdjustment *
282 st_adjustment_new (gdouble value,
283 gdouble lower,
284 gdouble upper,
285 gdouble step_increment,
286 gdouble page_increment,
287 gdouble page_size)
288 {
289 return g_object_new (ST_TYPE_ADJUSTMENT,
290 "value", value,
291 "lower", lower,
292 "upper", upper,
293 "step-increment", step_increment,
294 "page-increment", page_increment,
295 "page-size", page_size,
296 NULL);
297 }
298
299 gdouble
300 st_adjustment_get_value (StAdjustment *adjustment)
301 {
302 StAdjustmentPrivate *priv;
303
304 g_return_val_if_fail (ST_IS_ADJUSTMENT (adjustment), 0);
305
306 priv = adjustment->priv;
307
308 return priv->value;
309 }
310
311 void
312 st_adjustment_set_value (StAdjustment *adjustment,
313 gdouble value)
314 {
315 StAdjustmentPrivate *priv;
316
317 g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
318
319 priv = adjustment->priv;
320
321 /* Defer clamp until after construction. */
322 if (!priv->is_constructing)
323 {
324 value = CLAMP (value,
325 priv->lower,
326 MAX (priv->lower, priv->upper - priv->page_size));
327 }
328
329 if (priv->value != value)
330 {
331 priv->value = value;
332
333 g_object_notify (G_OBJECT (adjustment), "value");
334 }
335 }
336
337 void
338 st_adjustment_clamp_page (StAdjustment *adjustment,
339 gdouble lower,
340 gdouble upper)
341 {
342 StAdjustmentPrivate *priv;
343 gboolean changed;
344
345 g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
346
347 priv = adjustment->priv;
348
349 lower = CLAMP (lower, priv->lower, priv->upper - priv->page_size);
350 upper = CLAMP (upper, priv->lower + priv->page_size, priv->upper);
351
352 changed = FALSE;
353
354 if (priv->value + priv->page_size > upper)
355 {
356 priv->value = upper - priv->page_size;
357 changed = TRUE;
358 }
359
360 if (priv->value < lower)
361 {
362 priv->value = lower;
363 changed = TRUE;
364 }
365
366 if (changed)
367 g_object_notify (G_OBJECT (adjustment), "value");
368 }
369
370 static gboolean
371 st_adjustment_set_lower (StAdjustment *adjustment,
372 gdouble lower)
373 {
374 StAdjustmentPrivate *priv = adjustment->priv;
375
376 if (priv->lower != lower)
377 {
378 priv->lower = lower;
379
380 g_signal_emit (adjustment, signals[CHANGED], 0);
381
382 g_object_notify (G_OBJECT (adjustment), "lower");
383
384 /* Defer clamp until after construction. */
385 if (!priv->is_constructing)
386 st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
387
388 return TRUE;
389 }
390
391 return FALSE;
392 }
393
394 static gboolean
395 st_adjustment_set_upper (StAdjustment *adjustment,
396 gdouble upper)
397 {
398 StAdjustmentPrivate *priv = adjustment->priv;
399
400 if (priv->upper != upper)
401 {
402 priv->upper = upper;
403
404 g_signal_emit (adjustment, signals[CHANGED], 0);
405
406 g_object_notify (G_OBJECT (adjustment), "upper");
407
408 /* Defer clamp until after construction. */
409 if (!priv->is_constructing)
410 st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
411
412 return TRUE;
413 }
414
415 return FALSE;
416 }
417
418 static gboolean
419 st_adjustment_set_step_increment (StAdjustment *adjustment,
420 gdouble step)
421 {
422 StAdjustmentPrivate *priv = adjustment->priv;
423
424 if (priv->step_increment != step)
425 {
426 priv->step_increment = step;
427
428 g_signal_emit (adjustment, signals[CHANGED], 0);
429
430 g_object_notify (G_OBJECT (adjustment), "step-increment");
431
432 return TRUE;
433 }
434
435 return FALSE;
436 }
437
438 static gboolean
439 st_adjustment_set_page_increment (StAdjustment *adjustment,
440 gdouble page)
441 {
442 StAdjustmentPrivate *priv = adjustment->priv;
443
444 if (priv->page_increment != page)
445 {
446 priv->page_increment = page;
447
448 g_signal_emit (adjustment, signals[CHANGED], 0);
449
450 g_object_notify (G_OBJECT (adjustment), "page-increment");
451
452 return TRUE;
453 }
454
455 return FALSE;
456 }
457
458 static gboolean
459 st_adjustment_set_page_size (StAdjustment *adjustment,
460 gdouble size)
461 {
462 StAdjustmentPrivate *priv = adjustment->priv;
463
464 if (priv->page_size != size)
465 {
466 priv->page_size = size;
467
468 g_signal_emit (adjustment, signals[CHANGED], 0);
469
470 g_object_notify (G_OBJECT (adjustment), "page_size");
471
472 /* Well explicitely clamp after construction. */
473 if (!priv->is_constructing)
474 st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
475
476 return TRUE;
477 }
478
479 return FALSE;
480 }
481
482 void
483 st_adjustment_set_values (StAdjustment *adjustment,
484 gdouble value,
485 gdouble lower,
486 gdouble upper,
487 gdouble step_increment,
488 gdouble page_increment,
489 gdouble page_size)
490 {
491 StAdjustmentPrivate *priv;
492 gboolean emit_changed = FALSE;
493
494 g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
495 g_return_if_fail (page_size >= 0 && page_size <= G_MAXDOUBLE);
496 g_return_if_fail (step_increment >= 0 && step_increment <= G_MAXDOUBLE);
497 g_return_if_fail (page_increment >= 0 && page_increment <= G_MAXDOUBLE);
498
499 priv = adjustment->priv;
500
501 emit_changed = FALSE;
502
503 g_object_freeze_notify (G_OBJECT (adjustment));
504
505 emit_changed |= st_adjustment_set_lower (adjustment, lower);
506 emit_changed |= st_adjustment_set_upper (adjustment, upper);
507 emit_changed |= st_adjustment_set_step_increment (adjustment, step_increment);
508 emit_changed |= st_adjustment_set_page_increment (adjustment, page_increment);
509 emit_changed |= st_adjustment_set_page_size (adjustment, page_size);
510
511 if (value != priv->value)
512 {
513 st_adjustment_set_value (adjustment, value);
514 emit_changed = TRUE;
515 }
516
517 if (emit_changed)
518 g_signal_emit (G_OBJECT (adjustment), signals[CHANGED], 0);
519
520 g_object_thaw_notify (G_OBJECT (adjustment));
521 }
522
523 /**
524 * st_adjustment_get_values:
525 * @adjustment: an #StAdjustment
526 * @value: (out): the current value
527 * @lower: (out): the lower bound
528 * @upper: (out): the upper bound
529 * @step_increment: (out): the step increment
530 * @page_increment: (out): the page increment
531 * @page_size: (out): the page size
532 *
533 * Gets all of @adjustment's values at once.
534 */
535 void
536 st_adjustment_get_values (StAdjustment *adjustment,
537 gdouble *value,
538 gdouble *lower,
539 gdouble *upper,
540 gdouble *step_increment,
541 gdouble *page_increment,
542 gdouble *page_size)
543 {
544 StAdjustmentPrivate *priv;
545
546 g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
547
548 priv = adjustment->priv;
549
550 if (lower)
551 *lower = priv->lower;
552
553 if (upper)
554 *upper = priv->upper;
555
556 if (value)
557 *value = st_adjustment_get_value (adjustment);
558
559 if (step_increment)
560 *step_increment = priv->step_increment;
561
562 if (page_increment)
563 *page_increment = priv->page_increment;
564
565 if (page_size)
566 *page_size = priv->page_size;
567 }