1 | /* GTK - The GIMP Toolkit |
2 | * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | /* |
19 | * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS |
20 | * file for a list of people on the GTK+ Team. See the ChangeLog |
21 | * files for a list of changes. These files are distributed with |
22 | * GTK+ at ftp://ftp.gtk.org/pub/gtk/. |
23 | */ |
24 | |
25 | #include "config.h" |
26 | #include "gtkadjustment.h" |
27 | #include "gtkadjustmentprivate.h" |
28 | #include "gtkmarshalers.h" |
29 | #include "gtkprivate.h" |
30 | #include "gtkintl.h" |
31 | |
32 | |
33 | /** |
34 | * GtkAdjustment: |
35 | * |
36 | * `GtkAdjustment` is a model for a numeric value. |
37 | * |
38 | * The `GtkAdjustment has an associated lower and upper bound. |
39 | * It also contains step and page increments, and a page size. |
40 | * |
41 | * Adjustments are used within several GTK widgets, including |
42 | * [class@Gtk.SpinButton], [class@Gtk.Viewport], [class@Gtk.Scrollbar] |
43 | * and [class@Gtk.Scale]. |
44 | * |
45 | * The `GtkAdjustment` object does not update the value itself. Instead |
46 | * it is left up to the owner of the `GtkAdjustment` to control the value. |
47 | */ |
48 | |
49 | |
50 | struct _GtkAdjustmentPrivate { |
51 | double lower; |
52 | double upper; |
53 | double value; |
54 | double step_increment; |
55 | double page_increment; |
56 | double page_size; |
57 | |
58 | double source; |
59 | double target; |
60 | |
61 | guint duration; |
62 | guint tick_id; |
63 | gint64 start_time; |
64 | gint64 end_time; |
65 | GdkFrameClock *clock; |
66 | }; |
67 | typedef struct _GtkAdjustmentPrivate GtkAdjustmentPrivate; |
68 | |
69 | enum |
70 | { |
71 | PROP_0, |
72 | PROP_VALUE, |
73 | PROP_LOWER, |
74 | PROP_UPPER, |
75 | PROP_STEP_INCREMENT, |
76 | PROP_PAGE_INCREMENT, |
77 | PROP_PAGE_SIZE, |
78 | NUM_PROPERTIES |
79 | }; |
80 | |
81 | enum |
82 | { |
83 | CHANGED, |
84 | VALUE_CHANGED, |
85 | LAST_SIGNAL |
86 | }; |
87 | |
88 | |
89 | static void gtk_adjustment_get_property (GObject *object, |
90 | guint prop_id, |
91 | GValue *value, |
92 | GParamSpec *pspec); |
93 | static void gtk_adjustment_set_property (GObject *object, |
94 | guint prop_id, |
95 | const GValue *value, |
96 | GParamSpec *pspec); |
97 | static void gtk_adjustment_dispatch_properties_changed (GObject *object, |
98 | guint n_pspecs, |
99 | GParamSpec **pspecs); |
100 | |
101 | static guint adjustment_signals[LAST_SIGNAL] = { 0 }; |
102 | |
103 | static GParamSpec *adjustment_props[NUM_PROPERTIES] = { NULL, }; |
104 | |
105 | G_DEFINE_TYPE_WITH_PRIVATE (GtkAdjustment, gtk_adjustment, G_TYPE_INITIALLY_UNOWNED) |
106 | |
107 | static void |
108 | gtk_adjustment_finalize (GObject *object) |
109 | { |
110 | GtkAdjustment *adjustment = GTK_ADJUSTMENT (object); |
111 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
112 | |
113 | if (priv->tick_id) |
114 | g_signal_handler_disconnect (instance: priv->clock, handler_id: priv->tick_id); |
115 | if (priv->clock) |
116 | g_object_unref (object: priv->clock); |
117 | |
118 | G_OBJECT_CLASS (gtk_adjustment_parent_class)->finalize (object); |
119 | } |
120 | |
121 | static void |
122 | gtk_adjustment_class_init (GtkAdjustmentClass *class) |
123 | { |
124 | GObjectClass *gobject_class = G_OBJECT_CLASS (class); |
125 | |
126 | gobject_class->finalize = gtk_adjustment_finalize; |
127 | gobject_class->set_property = gtk_adjustment_set_property; |
128 | gobject_class->get_property = gtk_adjustment_get_property; |
129 | gobject_class->dispatch_properties_changed = gtk_adjustment_dispatch_properties_changed; |
130 | |
131 | class->changed = NULL; |
132 | class->value_changed = NULL; |
133 | |
134 | /** |
135 | * GtkAdjustment:value: (attributes org.gtk.Property.get=gtk_adjustment_get_value org.gtk.Property.set=gtk_adjustment_set_value) |
136 | * |
137 | * The value of the adjustment. |
138 | */ |
139 | adjustment_props[PROP_VALUE] = |
140 | g_param_spec_double (name: "value" , |
141 | P_("Value" ), |
142 | P_("The value of the adjustment" ), |
143 | minimum: -G_MAXDOUBLE, G_MAXDOUBLE, |
144 | default_value: 0.0, |
145 | GTK_PARAM_READWRITE); |
146 | |
147 | /** |
148 | * GtkAdjustment:lower: (attributes org.gtk.Property.get=gtk_adjustment_get_lower org.gtk.Property.set=gtk_adjustment_set_lower) |
149 | * |
150 | * The minimum value of the adjustment. |
151 | */ |
152 | adjustment_props[PROP_LOWER] = |
153 | g_param_spec_double (name: "lower" , |
154 | P_("Minimum Value" ), |
155 | P_("The minimum value of the adjustment" ), |
156 | minimum: -G_MAXDOUBLE, G_MAXDOUBLE, |
157 | default_value: 0.0, |
158 | GTK_PARAM_READWRITE); |
159 | |
160 | /** |
161 | * GtkAdjustment:upper: (attributes org.gtk.Property.get=gtk_adjustment_get_upper org.gtk.Property.set=gtk_adjustment_set_upper) |
162 | * |
163 | * The maximum value of the adjustment. |
164 | * |
165 | * Note that values will be restricted by `upper - page-size` if the page-size |
166 | * property is nonzero. |
167 | */ |
168 | adjustment_props[PROP_UPPER] = |
169 | g_param_spec_double (name: "upper" , |
170 | P_("Maximum Value" ), |
171 | P_("The maximum value of the adjustment" ), |
172 | minimum: -G_MAXDOUBLE, G_MAXDOUBLE, |
173 | default_value: 0.0, |
174 | GTK_PARAM_READWRITE); |
175 | |
176 | /** |
177 | * GtkAdjustment:step-increment: (attributes org.gtk.Property.get=gtk_adjustment_get_step_increment org.gtk.Property.set=gtk_adjustment_set_step_increment) |
178 | * |
179 | * The step increment of the adjustment. |
180 | */ |
181 | adjustment_props[PROP_STEP_INCREMENT] = |
182 | g_param_spec_double (name: "step-increment" , |
183 | P_("Step Increment" ), |
184 | P_("The step increment of the adjustment" ), |
185 | minimum: -G_MAXDOUBLE, G_MAXDOUBLE, |
186 | default_value: 0.0, |
187 | GTK_PARAM_READWRITE); |
188 | |
189 | /** |
190 | * GtkAdjustment:page-increment: (attributes org.gtk.Property.get=gtk_adjustment_get_page_increment org.gtk.Property.set=gtk_adjustment_set_page_increment) |
191 | * |
192 | * The page increment of the adjustment. |
193 | */ |
194 | adjustment_props[PROP_PAGE_INCREMENT] = |
195 | g_param_spec_double (name: "page-increment" , |
196 | P_("Page Increment" ), |
197 | P_("The page increment of the adjustment" ), |
198 | minimum: -G_MAXDOUBLE, G_MAXDOUBLE, |
199 | default_value: 0.0, |
200 | GTK_PARAM_READWRITE); |
201 | |
202 | /** |
203 | * GtkAdjustment:page-size: (attributes org.gtk.Property.get=gtk_adjustment_get_page_size org.gtk.Property.set=gtk_adjustment_set_page_size) |
204 | * |
205 | * The page size of the adjustment. |
206 | * |
207 | * Note that the page-size is irrelevant and should be set to zero |
208 | * if the adjustment is used for a simple scalar value, e.g. in a |
209 | * `GtkSpinButton`. |
210 | */ |
211 | adjustment_props[PROP_PAGE_SIZE] = |
212 | g_param_spec_double (name: "page-size" , |
213 | P_("Page Size" ), |
214 | P_("The page size of the adjustment" ), |
215 | minimum: -G_MAXDOUBLE, G_MAXDOUBLE, |
216 | default_value: 0.0, |
217 | GTK_PARAM_READWRITE); |
218 | |
219 | g_object_class_install_properties (oclass: gobject_class, n_pspecs: NUM_PROPERTIES, pspecs: adjustment_props); |
220 | |
221 | /** |
222 | * GtkAdjustment::changed: |
223 | * @adjustment: the object which received the signal |
224 | * |
225 | * Emitted when one or more of the `GtkAdjustment` properties have been |
226 | * changed. |
227 | * |
228 | * Note that the [property@Gtk.Adjustment:value] property is |
229 | * covered by the [signal@Gtk.Adjustment::value-changed] signal. |
230 | */ |
231 | adjustment_signals[CHANGED] = |
232 | g_signal_new (I_("changed" ), |
233 | G_OBJECT_CLASS_TYPE (class), |
234 | signal_flags: G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, |
235 | G_STRUCT_OFFSET (GtkAdjustmentClass, changed), |
236 | NULL, NULL, |
237 | NULL, |
238 | G_TYPE_NONE, n_params: 0); |
239 | |
240 | /** |
241 | * GtkAdjustment::value-changed: |
242 | * @adjustment: the object which received the signal |
243 | * |
244 | * Emitted when the value has been changed. |
245 | */ |
246 | adjustment_signals[VALUE_CHANGED] = |
247 | g_signal_new (I_("value-changed" ), |
248 | G_OBJECT_CLASS_TYPE (class), |
249 | signal_flags: G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, |
250 | G_STRUCT_OFFSET (GtkAdjustmentClass, value_changed), |
251 | NULL, NULL, |
252 | NULL, |
253 | G_TYPE_NONE, n_params: 0); |
254 | } |
255 | |
256 | static void |
257 | gtk_adjustment_init (GtkAdjustment *adjustment) |
258 | { |
259 | } |
260 | |
261 | static void |
262 | gtk_adjustment_get_property (GObject *object, |
263 | guint prop_id, |
264 | GValue *value, |
265 | GParamSpec *pspec) |
266 | { |
267 | GtkAdjustment *adjustment = GTK_ADJUSTMENT (object); |
268 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
269 | |
270 | switch (prop_id) |
271 | { |
272 | case PROP_VALUE: |
273 | g_value_set_double (value, v_double: priv->value); |
274 | break; |
275 | case PROP_LOWER: |
276 | g_value_set_double (value, v_double: priv->lower); |
277 | break; |
278 | case PROP_UPPER: |
279 | g_value_set_double (value, v_double: priv->upper); |
280 | break; |
281 | case PROP_STEP_INCREMENT: |
282 | g_value_set_double (value, v_double: priv->step_increment); |
283 | break; |
284 | case PROP_PAGE_INCREMENT: |
285 | g_value_set_double (value, v_double: priv->page_increment); |
286 | break; |
287 | case PROP_PAGE_SIZE: |
288 | g_value_set_double (value, v_double: priv->page_size); |
289 | break; |
290 | default: |
291 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
292 | break; |
293 | } |
294 | } |
295 | |
296 | static void |
297 | gtk_adjustment_set_property (GObject *object, |
298 | guint prop_id, |
299 | const GValue *value, |
300 | GParamSpec *pspec) |
301 | { |
302 | GtkAdjustment *adjustment = GTK_ADJUSTMENT (object); |
303 | double double_value = g_value_get_double (value); |
304 | |
305 | switch (prop_id) |
306 | { |
307 | case PROP_VALUE: |
308 | gtk_adjustment_set_value (adjustment, value: double_value); |
309 | break; |
310 | case PROP_LOWER: |
311 | gtk_adjustment_set_lower (adjustment, lower: double_value); |
312 | break; |
313 | case PROP_UPPER: |
314 | gtk_adjustment_set_upper (adjustment, upper: double_value); |
315 | break; |
316 | case PROP_STEP_INCREMENT: |
317 | gtk_adjustment_set_step_increment (adjustment, step_increment: double_value); |
318 | break; |
319 | case PROP_PAGE_INCREMENT: |
320 | gtk_adjustment_set_page_increment (adjustment, page_increment: double_value); |
321 | break; |
322 | case PROP_PAGE_SIZE: |
323 | gtk_adjustment_set_page_size (adjustment, page_size: double_value); |
324 | break; |
325 | default: |
326 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
327 | break; |
328 | } |
329 | } |
330 | |
331 | static inline void |
332 | emit_value_changed (GtkAdjustment *adjustment) |
333 | { |
334 | g_signal_emit (instance: adjustment, signal_id: adjustment_signals[VALUE_CHANGED], detail: 0); |
335 | g_object_notify_by_pspec (G_OBJECT (adjustment), pspec: adjustment_props[PROP_VALUE]); |
336 | } |
337 | |
338 | static void |
339 | gtk_adjustment_dispatch_properties_changed (GObject *object, |
340 | guint n_pspecs, |
341 | GParamSpec **pspecs) |
342 | { |
343 | gboolean changed = FALSE; |
344 | int i; |
345 | |
346 | G_OBJECT_CLASS (gtk_adjustment_parent_class)->dispatch_properties_changed (object, n_pspecs, pspecs); |
347 | |
348 | for (i = 0; i < n_pspecs; i++) |
349 | switch (pspecs[i]->param_id) |
350 | { |
351 | case PROP_LOWER: |
352 | case PROP_UPPER: |
353 | case PROP_STEP_INCREMENT: |
354 | case PROP_PAGE_INCREMENT: |
355 | case PROP_PAGE_SIZE: |
356 | changed = TRUE; |
357 | break; |
358 | default: |
359 | break; |
360 | } |
361 | |
362 | if (changed) |
363 | { |
364 | g_signal_emit (instance: object, signal_id: adjustment_signals[CHANGED], detail: 0); |
365 | } |
366 | } |
367 | |
368 | /** |
369 | * gtk_adjustment_new: |
370 | * @value: the initial value |
371 | * @lower: the minimum value |
372 | * @upper: the maximum value |
373 | * @step_increment: the step increment |
374 | * @page_increment: the page increment |
375 | * @page_size: the page size |
376 | * |
377 | * Creates a new `GtkAdjustment`. |
378 | * |
379 | * Returns: a new `GtkAdjustment` |
380 | */ |
381 | GtkAdjustment * |
382 | gtk_adjustment_new (double value, |
383 | double lower, |
384 | double upper, |
385 | double step_increment, |
386 | double page_increment, |
387 | double page_size) |
388 | { |
389 | return g_object_new (GTK_TYPE_ADJUSTMENT, |
390 | first_property_name: "lower" , lower, |
391 | "upper" , upper, |
392 | "step-increment" , step_increment, |
393 | "page-increment" , page_increment, |
394 | "page-size" , page_size, |
395 | "value" , value, |
396 | NULL); |
397 | } |
398 | |
399 | /** |
400 | * gtk_adjustment_get_value: (attributes org.gtk.Method.get_property=value) |
401 | * @adjustment: a `GtkAdjustment` |
402 | * |
403 | * Gets the current value of the adjustment. |
404 | * |
405 | * Returns: The current value of the adjustment |
406 | */ |
407 | double |
408 | gtk_adjustment_get_value (GtkAdjustment *adjustment) |
409 | { |
410 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
411 | g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0); |
412 | |
413 | return priv->value; |
414 | } |
415 | |
416 | double |
417 | gtk_adjustment_get_target_value (GtkAdjustment *adjustment) |
418 | { |
419 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
420 | |
421 | g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0); |
422 | |
423 | if (priv->tick_id) |
424 | return priv->target; |
425 | else |
426 | return priv->value; |
427 | } |
428 | |
429 | static void |
430 | adjustment_set_value (GtkAdjustment *adjustment, |
431 | double value) |
432 | { |
433 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
434 | |
435 | if (priv->value != value) |
436 | { |
437 | priv->value = value; |
438 | emit_value_changed (adjustment); |
439 | } |
440 | } |
441 | |
442 | static void gtk_adjustment_on_frame_clock_update (GdkFrameClock *clock, |
443 | GtkAdjustment *adjustment); |
444 | |
445 | static void |
446 | gtk_adjustment_begin_updating (GtkAdjustment *adjustment) |
447 | { |
448 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
449 | |
450 | if (priv->tick_id == 0) |
451 | { |
452 | priv->tick_id = g_signal_connect (priv->clock, "update" , |
453 | G_CALLBACK (gtk_adjustment_on_frame_clock_update), adjustment); |
454 | gdk_frame_clock_begin_updating (frame_clock: priv->clock); |
455 | } |
456 | } |
457 | |
458 | static void |
459 | gtk_adjustment_end_updating (GtkAdjustment *adjustment) |
460 | { |
461 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
462 | |
463 | if (priv->tick_id != 0) |
464 | { |
465 | g_signal_handler_disconnect (instance: priv->clock, handler_id: priv->tick_id); |
466 | priv->tick_id = 0; |
467 | gdk_frame_clock_end_updating (frame_clock: priv->clock); |
468 | } |
469 | } |
470 | |
471 | /* From clutter-easing.c, based on Robert Penner's |
472 | * infamous easing equations, MIT license. |
473 | */ |
474 | static double |
475 | ease_out_cubic (double t) |
476 | { |
477 | double p = t - 1; |
478 | |
479 | return p * p * p + 1; |
480 | } |
481 | |
482 | static void |
483 | gtk_adjustment_on_frame_clock_update (GdkFrameClock *clock, |
484 | GtkAdjustment *adjustment) |
485 | { |
486 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
487 | gint64 now; |
488 | |
489 | now = gdk_frame_clock_get_frame_time (frame_clock: clock); |
490 | |
491 | if (now < priv->end_time) |
492 | { |
493 | double t; |
494 | |
495 | t = (now - priv->start_time) / (double) (priv->end_time - priv->start_time); |
496 | t = ease_out_cubic (t); |
497 | adjustment_set_value (adjustment, value: priv->source + t * (priv->target - priv->source)); |
498 | } |
499 | else |
500 | { |
501 | adjustment_set_value (adjustment, value: priv->target); |
502 | gtk_adjustment_end_updating (adjustment); |
503 | } |
504 | } |
505 | |
506 | static void |
507 | gtk_adjustment_set_value_internal (GtkAdjustment *adjustment, |
508 | double value, |
509 | gboolean animate) |
510 | { |
511 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
512 | |
513 | /* don't use CLAMP() so we don't end up below lower if upper - page_size |
514 | * is smaller than lower |
515 | */ |
516 | value = MIN (value, priv->upper - priv->page_size); |
517 | value = MAX (value, priv->lower); |
518 | |
519 | if (animate && priv->duration != 0 && priv->clock != NULL) |
520 | { |
521 | if (priv->tick_id && priv->target == value) |
522 | return; |
523 | |
524 | priv->source = priv->value; |
525 | priv->target = value; |
526 | priv->start_time = gdk_frame_clock_get_frame_time (frame_clock: priv->clock); |
527 | priv->end_time = priv->start_time + 1000 * priv->duration; |
528 | gtk_adjustment_begin_updating (adjustment); |
529 | } |
530 | else |
531 | { |
532 | gtk_adjustment_end_updating (adjustment); |
533 | adjustment_set_value (adjustment, value); |
534 | } |
535 | } |
536 | |
537 | /** |
538 | * gtk_adjustment_set_value: (attributes org.gtk.Method.set_property=value) |
539 | * @adjustment: a `GtkAdjustment` |
540 | * @value: the new value |
541 | * |
542 | * Sets the `GtkAdjustment` value. |
543 | * |
544 | * The value is clamped to lie between [property@Gtk.Adjustment:lower] |
545 | * and [property@Gtk.Adjustment:upper]. |
546 | * |
547 | * Note that for adjustments which are used in a `GtkScrollbar`, |
548 | * the effective range of allowed values goes from |
549 | * [property@Gtk.Adjustment:lower] to |
550 | * [property@Gtk.Adjustment:upper] - [property@Gtk.Adjustment:page-size]. |
551 | */ |
552 | void |
553 | gtk_adjustment_set_value (GtkAdjustment *adjustment, |
554 | double value) |
555 | { |
556 | g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); |
557 | |
558 | gtk_adjustment_set_value_internal (adjustment, value, FALSE); |
559 | } |
560 | |
561 | void |
562 | gtk_adjustment_animate_to_value (GtkAdjustment *adjustment, |
563 | double value) |
564 | { |
565 | g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); |
566 | |
567 | gtk_adjustment_set_value_internal (adjustment, value, TRUE); |
568 | } |
569 | |
570 | /** |
571 | * gtk_adjustment_get_lower: (attributes org.gtk.Method.get_property=lower) |
572 | * @adjustment: a `GtkAdjustment` |
573 | * |
574 | * Retrieves the minimum value of the adjustment. |
575 | * |
576 | * Returns: The current minimum value of the adjustment |
577 | **/ |
578 | double |
579 | gtk_adjustment_get_lower (GtkAdjustment *adjustment) |
580 | { |
581 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
582 | |
583 | g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0); |
584 | |
585 | return priv->lower; |
586 | } |
587 | |
588 | /** |
589 | * gtk_adjustment_set_lower: (attributes org.gtk.Method.set_property=lower) |
590 | * @adjustment: a `GtkAdjustment` |
591 | * @lower: the new minimum value |
592 | * |
593 | * Sets the minimum value of the adjustment. |
594 | * |
595 | * When setting multiple adjustment properties via their individual |
596 | * setters, multiple [signal@Gtk.Adjustment::changed] signals will |
597 | * be emitted. However, since the emission of the |
598 | * [signal@Gtk.Adjustment::changed] signal is tied to the emission |
599 | * of the ::notify signals of the changed properties, it’s possible |
600 | * to compress the [signal@Gtk.Adjustment::changed] signals into one |
601 | * by calling g_object_freeze_notify() and g_object_thaw_notify() |
602 | * around the calls to the individual setters. |
603 | * |
604 | * Alternatively, using a single g_object_set() for all the properties |
605 | * to change, or using [method@Gtk.Adjustment.configure] has the same effect. |
606 | */ |
607 | void |
608 | gtk_adjustment_set_lower (GtkAdjustment *adjustment, |
609 | double lower) |
610 | { |
611 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
612 | |
613 | g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); |
614 | |
615 | if (lower != priv->lower) |
616 | { |
617 | priv->lower = lower; |
618 | g_object_notify_by_pspec (G_OBJECT (adjustment), pspec: adjustment_props[PROP_LOWER]); |
619 | } |
620 | } |
621 | |
622 | /** |
623 | * gtk_adjustment_get_upper: (attributes org.gtk.Method.get_property=upper) |
624 | * @adjustment: a `GtkAdjustment` |
625 | * |
626 | * Retrieves the maximum value of the adjustment. |
627 | * |
628 | * Returns: The current maximum value of the adjustment |
629 | */ |
630 | double |
631 | gtk_adjustment_get_upper (GtkAdjustment *adjustment) |
632 | { |
633 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
634 | |
635 | g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0); |
636 | |
637 | return priv->upper; |
638 | } |
639 | |
640 | /** |
641 | * gtk_adjustment_set_upper: (attributes org.gtk.Method.set_property=upper) |
642 | * @adjustment: a `GtkAdjustment` |
643 | * @upper: the new maximum value |
644 | * |
645 | * Sets the maximum value of the adjustment. |
646 | * |
647 | * Note that values will be restricted by `upper - page-size` |
648 | * if the page-size property is nonzero. |
649 | * |
650 | * See [method@Gtk.Adjustment.set_lower] about how to compress |
651 | * multiple emissions of the [signal@Gtk.Adjustment::changed] |
652 | * signal when setting multiple adjustment properties. |
653 | */ |
654 | void |
655 | gtk_adjustment_set_upper (GtkAdjustment *adjustment, |
656 | double upper) |
657 | { |
658 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
659 | |
660 | g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); |
661 | |
662 | if (upper != priv->upper) |
663 | { |
664 | priv->upper = upper; |
665 | g_object_notify_by_pspec (G_OBJECT (adjustment), pspec: adjustment_props[PROP_UPPER]); |
666 | } |
667 | } |
668 | |
669 | /** |
670 | * gtk_adjustment_get_step_increment: (attributes org.gtk.Method.get_property=step-increment) |
671 | * @adjustment: a `GtkAdjustment` |
672 | * |
673 | * Retrieves the step increment of the adjustment. |
674 | * |
675 | * Returns: The current step increment of the adjustment. |
676 | */ |
677 | double |
678 | gtk_adjustment_get_step_increment (GtkAdjustment *adjustment) |
679 | { |
680 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
681 | |
682 | g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0); |
683 | |
684 | return priv->step_increment; |
685 | } |
686 | |
687 | /** |
688 | * gtk_adjustment_set_step_increment: (attributes org.gtk.Method.set_property=step-increment) |
689 | * @adjustment: a `GtkAdjustment` |
690 | * @step_increment: the new step increment |
691 | * |
692 | * Sets the step increment of the adjustment. |
693 | * |
694 | * See [method@Gtk.Adjustment.set_lower] about how to compress |
695 | * multiple emissions of the [signal@Gtk.Adjustment::changed] |
696 | * signal when setting multiple adjustment properties. |
697 | */ |
698 | void |
699 | gtk_adjustment_set_step_increment (GtkAdjustment *adjustment, |
700 | double step_increment) |
701 | { |
702 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
703 | |
704 | g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); |
705 | |
706 | if (step_increment != priv->step_increment) |
707 | { |
708 | priv->step_increment = step_increment; |
709 | g_object_notify_by_pspec (G_OBJECT (adjustment), pspec: adjustment_props[PROP_STEP_INCREMENT]); |
710 | } |
711 | } |
712 | |
713 | /** |
714 | * gtk_adjustment_get_page_increment: (attributes org.gtk.Method.get_property=page-increment) |
715 | * @adjustment: a `GtkAdjustment` |
716 | * |
717 | * Retrieves the page increment of the adjustment. |
718 | * |
719 | * Returns: The current page increment of the adjustment |
720 | **/ |
721 | double |
722 | gtk_adjustment_get_page_increment (GtkAdjustment *adjustment) |
723 | { |
724 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
725 | |
726 | g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0); |
727 | |
728 | return priv->page_increment; |
729 | } |
730 | |
731 | /** |
732 | * gtk_adjustment_set_page_increment: (attributes org.gtk.Method.set_property=page-increment) |
733 | * @adjustment: a `GtkAdjustment` |
734 | * @page_increment: the new page increment |
735 | * |
736 | * Sets the page increment of the adjustment. |
737 | * |
738 | * See [method@Gtk.Adjustment.set_lower] about how to compress |
739 | * multiple emissions of the [signal@Gtk.Adjustment::changed] |
740 | * signal when setting multiple adjustment properties. |
741 | */ |
742 | void |
743 | gtk_adjustment_set_page_increment (GtkAdjustment *adjustment, |
744 | double page_increment) |
745 | { |
746 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
747 | |
748 | g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); |
749 | |
750 | if (page_increment != priv->page_increment) |
751 | { |
752 | priv->page_increment = page_increment; |
753 | g_object_notify_by_pspec (G_OBJECT (adjustment), pspec: adjustment_props[PROP_PAGE_INCREMENT]); |
754 | } |
755 | } |
756 | |
757 | /** |
758 | * gtk_adjustment_get_page_size: (attributes org.gtk.Method.get_property=page-size) |
759 | * @adjustment: a `GtkAdjustment` |
760 | * |
761 | * Retrieves the page size of the adjustment. |
762 | * |
763 | * Returns: The current page size of the adjustment |
764 | **/ |
765 | double |
766 | gtk_adjustment_get_page_size (GtkAdjustment *adjustment) |
767 | { |
768 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
769 | |
770 | g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0); |
771 | |
772 | return priv->page_size; |
773 | } |
774 | |
775 | /** |
776 | * gtk_adjustment_set_page_size: (attributes org.gtk.Method.set_property=page-size) |
777 | * @adjustment: a `GtkAdjustment` |
778 | * @page_size: the new page size |
779 | * |
780 | * Sets the page size of the adjustment. |
781 | * |
782 | * See [method@Gtk.Adjustment.set_lower] about how to compress |
783 | * multiple emissions of the [signal@Gtk.Adjustment::changed] |
784 | * signal when setting multiple adjustment properties. |
785 | */ |
786 | void |
787 | gtk_adjustment_set_page_size (GtkAdjustment *adjustment, |
788 | double page_size) |
789 | { |
790 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
791 | |
792 | g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); |
793 | |
794 | if (page_size != priv->page_size) |
795 | { |
796 | priv->page_size = page_size; |
797 | g_object_notify_by_pspec (G_OBJECT (adjustment), pspec: adjustment_props[PROP_PAGE_SIZE]); |
798 | } |
799 | } |
800 | |
801 | /** |
802 | * gtk_adjustment_configure: |
803 | * @adjustment: a `GtkAdjustment` |
804 | * @value: the new value |
805 | * @lower: the new minimum value |
806 | * @upper: the new maximum value |
807 | * @step_increment: the new step increment |
808 | * @page_increment: the new page increment |
809 | * @page_size: the new page size |
810 | * |
811 | * Sets all properties of the adjustment at once. |
812 | * |
813 | * Use this function to avoid multiple emissions of the |
814 | * [signal@Gtk.Adjustment::changed] signal. See |
815 | * [method@Gtk.Adjustment.set_lower] for an alternative |
816 | * way of compressing multiple emissions of |
817 | * [signal@Gtk.Adjustment::changed] into one. |
818 | */ |
819 | void |
820 | gtk_adjustment_configure (GtkAdjustment *adjustment, |
821 | double value, |
822 | double lower, |
823 | double upper, |
824 | double step_increment, |
825 | double page_increment, |
826 | double page_size) |
827 | { |
828 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
829 | gboolean value_changed = FALSE; |
830 | |
831 | g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); |
832 | |
833 | g_object_freeze_notify (G_OBJECT (adjustment)); |
834 | |
835 | gtk_adjustment_set_lower (adjustment, lower); |
836 | gtk_adjustment_set_upper (adjustment, upper); |
837 | gtk_adjustment_set_step_increment (adjustment, step_increment); |
838 | gtk_adjustment_set_page_increment (adjustment, page_increment); |
839 | gtk_adjustment_set_page_size (adjustment, page_size); |
840 | |
841 | /* don't use CLAMP() so we don't end up below lower if upper - page_size |
842 | * is smaller than lower |
843 | */ |
844 | value = MIN (value, upper - page_size); |
845 | value = MAX (value, lower); |
846 | |
847 | if (value != priv->value) |
848 | { |
849 | /* set value manually to make sure "changed" is emitted with the |
850 | * new value in place and is emitted before "value-changed" |
851 | */ |
852 | priv->value = value; |
853 | value_changed = TRUE; |
854 | } |
855 | |
856 | /* The dispatch_properties_changed implementation will emit ::changed! */ |
857 | g_object_thaw_notify (G_OBJECT (adjustment)); |
858 | |
859 | if (value_changed) |
860 | emit_value_changed (adjustment); |
861 | } |
862 | |
863 | /** |
864 | * gtk_adjustment_clamp_page: |
865 | * @adjustment: a `GtkAdjustment` |
866 | * @lower: the lower value |
867 | * @upper: the upper value |
868 | * |
869 | * Updates the value property to ensure that the range |
870 | * between @lower and @upper is in the current page. |
871 | * |
872 | * The current page goes from `value` to `value` + `page-size`. |
873 | * If the range is larger than the page size, then only the |
874 | * start of it will be in the current page. |
875 | * |
876 | * A [signal@Gtk.Adjustment::value-changed] signal will be emitted |
877 | * if the value is changed. |
878 | */ |
879 | void |
880 | gtk_adjustment_clamp_page (GtkAdjustment *adjustment, |
881 | double lower, |
882 | double upper) |
883 | { |
884 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
885 | gboolean need_emission; |
886 | |
887 | g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment)); |
888 | |
889 | lower = CLAMP (lower, priv->lower, priv->upper); |
890 | upper = CLAMP (upper, priv->lower, priv->upper); |
891 | |
892 | need_emission = FALSE; |
893 | |
894 | if (priv->value + priv->page_size < upper) |
895 | { |
896 | priv->value = upper - priv->page_size; |
897 | need_emission = TRUE; |
898 | } |
899 | if (priv->value > lower) |
900 | { |
901 | priv->value = lower; |
902 | need_emission = TRUE; |
903 | } |
904 | |
905 | if (need_emission) |
906 | emit_value_changed (adjustment); |
907 | } |
908 | |
909 | /** |
910 | * gtk_adjustment_get_minimum_increment: |
911 | * @adjustment: a `GtkAdjustment` |
912 | * |
913 | * Gets the smaller of step increment and page increment. |
914 | * |
915 | * Returns: the minimum increment of @adjustment |
916 | */ |
917 | double |
918 | gtk_adjustment_get_minimum_increment (GtkAdjustment *adjustment) |
919 | { |
920 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
921 | double minimum_increment; |
922 | |
923 | g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0); |
924 | |
925 | if (priv->step_increment != 0 && priv->page_increment != 0) |
926 | { |
927 | if (ABS (priv->step_increment) < ABS (priv->page_increment)) |
928 | minimum_increment = priv->step_increment; |
929 | else |
930 | minimum_increment = priv->page_increment; |
931 | } |
932 | else if (priv->step_increment == 0 && priv->page_increment == 0) |
933 | { |
934 | minimum_increment = 0; |
935 | } |
936 | else if (priv->step_increment == 0) |
937 | { |
938 | minimum_increment = priv->page_increment; |
939 | } |
940 | else |
941 | { |
942 | minimum_increment = priv->step_increment; |
943 | } |
944 | |
945 | return minimum_increment; |
946 | } |
947 | |
948 | void |
949 | gtk_adjustment_enable_animation (GtkAdjustment *adjustment, |
950 | GdkFrameClock *clock, |
951 | guint duration) |
952 | { |
953 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
954 | |
955 | if (priv->clock != clock) |
956 | { |
957 | if (priv->tick_id) |
958 | { |
959 | adjustment_set_value (adjustment, value: priv->target); |
960 | |
961 | g_signal_handler_disconnect (instance: priv->clock, handler_id: priv->tick_id); |
962 | priv->tick_id = 0; |
963 | gdk_frame_clock_end_updating (frame_clock: priv->clock); |
964 | } |
965 | |
966 | if (priv->clock) |
967 | g_object_unref (object: priv->clock); |
968 | |
969 | priv->clock = clock; |
970 | |
971 | if (priv->clock) |
972 | g_object_ref (priv->clock); |
973 | } |
974 | |
975 | priv->duration = duration; |
976 | } |
977 | |
978 | guint |
979 | gtk_adjustment_get_animation_duration (GtkAdjustment *adjustment) |
980 | { |
981 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
982 | |
983 | return priv->duration; |
984 | } |
985 | |
986 | gboolean |
987 | gtk_adjustment_is_animating (GtkAdjustment *adjustment) |
988 | { |
989 | GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment); |
990 | |
991 | return priv->tick_id != 0; |
992 | } |
993 | |