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
50struct _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};
67typedef struct _GtkAdjustmentPrivate GtkAdjustmentPrivate;
68
69enum
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
81enum
82{
83 CHANGED,
84 VALUE_CHANGED,
85 LAST_SIGNAL
86};
87
88
89static void gtk_adjustment_get_property (GObject *object,
90 guint prop_id,
91 GValue *value,
92 GParamSpec *pspec);
93static void gtk_adjustment_set_property (GObject *object,
94 guint prop_id,
95 const GValue *value,
96 GParamSpec *pspec);
97static void gtk_adjustment_dispatch_properties_changed (GObject *object,
98 guint n_pspecs,
99 GParamSpec **pspecs);
100
101static guint adjustment_signals[LAST_SIGNAL] = { 0 };
102
103static GParamSpec *adjustment_props[NUM_PROPERTIES] = { NULL, };
104
105G_DEFINE_TYPE_WITH_PRIVATE (GtkAdjustment, gtk_adjustment, G_TYPE_INITIALLY_UNOWNED)
106
107static void
108gtk_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
121static void
122gtk_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
256static void
257gtk_adjustment_init (GtkAdjustment *adjustment)
258{
259}
260
261static void
262gtk_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
296static void
297gtk_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
331static inline void
332emit_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
338static void
339gtk_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 */
381GtkAdjustment *
382gtk_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 */
407double
408gtk_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
416double
417gtk_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
429static void
430adjustment_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
442static void gtk_adjustment_on_frame_clock_update (GdkFrameClock *clock,
443 GtkAdjustment *adjustment);
444
445static void
446gtk_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
458static void
459gtk_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 */
474static double
475ease_out_cubic (double t)
476{
477 double p = t - 1;
478
479 return p * p * p + 1;
480}
481
482static void
483gtk_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
506static void
507gtk_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 */
552void
553gtk_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
561void
562gtk_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 **/
578double
579gtk_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 */
607void
608gtk_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 */
630double
631gtk_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 */
654void
655gtk_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 */
677double
678gtk_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 */
698void
699gtk_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 **/
721double
722gtk_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 */
742void
743gtk_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 **/
765double
766gtk_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 */
786void
787gtk_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 */
819void
820gtk_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 */
879void
880gtk_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 */
917double
918gtk_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
948void
949gtk_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
978guint
979gtk_adjustment_get_animation_duration (GtkAdjustment *adjustment)
980{
981 GtkAdjustmentPrivate *priv = gtk_adjustment_get_instance_private (self: adjustment);
982
983 return priv->duration;
984}
985
986gboolean
987gtk_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

source code of gtk/gtk/gtkadjustment.c