]> Pileus Git - ~andy/gtk/blob - gtk/gtkadjustment.c
Bug 544858 – Seal GtkAdjustment
[~andy/gtk] / gtk / gtkadjustment.c
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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "config.h"
28 #include "gtkadjustment.h"
29 #include "gtkmarshalers.h"
30 #include "gtkprivate.h"
31 #include "gtkintl.h"
32 #include "gtkalias.h"
33
34 enum
35 {
36   PROP_0,
37   PROP_VALUE,
38   PROP_LOWER,
39   PROP_UPPER,
40   PROP_STEP_INCREMENT,
41   PROP_PAGE_INCREMENT,
42   PROP_PAGE_SIZE
43 };
44
45 enum
46 {
47   CHANGED,
48   VALUE_CHANGED,
49   LAST_SIGNAL
50 };
51
52
53 static void gtk_adjustment_get_property                (GObject      *object,
54                                                         guint         prop_id,
55                                                         GValue       *value,
56                                                         GParamSpec   *pspec);
57 static void gtk_adjustment_set_property                (GObject      *object,
58                                                         guint         prop_id,
59                                                         const GValue *value,
60                                                         GParamSpec   *pspec);
61 static void gtk_adjustment_dispatch_properties_changed (GObject      *object,
62                                                         guint         n_pspecs,
63                                                         GParamSpec  **pspecs);
64
65 static guint adjustment_signals[LAST_SIGNAL] = { 0 };
66
67 static guint64 adjustment_changed_stamp = 0; /* protected by global gdk lock */
68
69 G_DEFINE_TYPE (GtkAdjustment, gtk_adjustment, GTK_TYPE_OBJECT)
70
71 static void
72 gtk_adjustment_class_init (GtkAdjustmentClass *class)
73 {
74   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
75
76   gobject_class->set_property                = gtk_adjustment_set_property;
77   gobject_class->get_property                = gtk_adjustment_get_property;
78   gobject_class->dispatch_properties_changed = gtk_adjustment_dispatch_properties_changed;
79
80   class->changed = NULL;
81   class->value_changed = NULL;
82
83   /**
84    * GtkAdjustment:value:
85    * 
86    * The value of the adjustment.
87    * 
88    * Since: 2.4
89    */
90   g_object_class_install_property (gobject_class,
91                                    PROP_VALUE,
92                                    g_param_spec_double ("value",
93                                                         P_("Value"),
94                                                         P_("The value of the adjustment"),
95                                                         -G_MAXDOUBLE, 
96                                                         G_MAXDOUBLE, 
97                                                         0.0, 
98                                                         GTK_PARAM_READWRITE));
99   
100   /**
101    * GtkAdjustment:lower:
102    * 
103    * The minimum value of the adjustment.
104    * 
105    * Since: 2.4
106    */
107   g_object_class_install_property (gobject_class,
108                                    PROP_LOWER,
109                                    g_param_spec_double ("lower",
110                                                         P_("Minimum Value"),
111                                                         P_("The minimum value of the adjustment"),
112                                                         -G_MAXDOUBLE, 
113                                                         G_MAXDOUBLE, 
114                                                         0.0,
115                                                         GTK_PARAM_READWRITE));
116   
117   /**
118    * GtkAdjustment:upper:
119    * 
120    * The maximum value of the adjustment. 
121    * Note that values will be restricted by 
122    * <literal>upper - page-size</literal> if the page-size 
123    * property is nonzero.
124    *
125    * Since: 2.4
126    */
127   g_object_class_install_property (gobject_class,
128                                    PROP_UPPER,
129                                    g_param_spec_double ("upper",
130                                                         P_("Maximum Value"),
131                                                         P_("The maximum value of the adjustment"),
132                                                         -G_MAXDOUBLE, 
133                                                         G_MAXDOUBLE, 
134                                                         0.0, 
135                                                         GTK_PARAM_READWRITE));
136   
137   /**
138    * GtkAdjustment:step-increment:
139    * 
140    * The step increment of the adjustment.
141    * 
142    * Since: 2.4
143    */
144   g_object_class_install_property (gobject_class,
145                                    PROP_STEP_INCREMENT,
146                                    g_param_spec_double ("step-increment",
147                                                         P_("Step Increment"),
148                                                         P_("The step increment of the adjustment"),
149                                                         -G_MAXDOUBLE, 
150                                                         G_MAXDOUBLE, 
151                                                         0.0, 
152                                                         GTK_PARAM_READWRITE));
153   
154   /**
155    * GtkAdjustment:page-increment:
156    * 
157    * The page increment of the adjustment.
158    * 
159    * Since: 2.4
160    */
161   g_object_class_install_property (gobject_class,
162                                    PROP_PAGE_INCREMENT,
163                                    g_param_spec_double ("page-increment",
164                                                         P_("Page Increment"),
165                                                         P_("The page increment of the adjustment"),
166                                                         -G_MAXDOUBLE, 
167                                                         G_MAXDOUBLE, 
168                                                         0.0, 
169                                                         GTK_PARAM_READWRITE));
170   
171   /**
172    * GtkAdjustment:page-size:
173    * 
174    * The page size of the adjustment. 
175    * Note that the page-size is irrelevant and should be set to zero
176    * if the adjustment is used for a simple scalar value, e.g. in a 
177    * #GtkSpinButton.
178    * 
179    * Since: 2.4
180    */
181   g_object_class_install_property (gobject_class,
182                                    PROP_PAGE_SIZE,
183                                    g_param_spec_double ("page-size",
184                                                         P_("Page Size"),
185                                                         P_("The page size of the adjustment"),
186                                                         -G_MAXDOUBLE, 
187                                                         G_MAXDOUBLE, 
188                                                         0.0, 
189                                                         GTK_PARAM_READWRITE));
190
191   adjustment_signals[CHANGED] =
192     g_signal_new (I_("changed"),
193                   G_OBJECT_CLASS_TYPE (class),
194                   G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
195                   G_STRUCT_OFFSET (GtkAdjustmentClass, changed),
196                   NULL, NULL,
197                   _gtk_marshal_VOID__VOID,
198                   G_TYPE_NONE, 0);
199
200   adjustment_signals[VALUE_CHANGED] =
201     g_signal_new (I_("value_changed"),
202                   G_OBJECT_CLASS_TYPE (class),
203                   G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
204                   G_STRUCT_OFFSET (GtkAdjustmentClass, value_changed),
205                   NULL, NULL,
206                   _gtk_marshal_VOID__VOID,
207                   G_TYPE_NONE, 0);
208 }
209
210 static void
211 gtk_adjustment_init (GtkAdjustment *adjustment)
212 {
213   adjustment->value = 0.0;
214   adjustment->lower = 0.0;
215   adjustment->upper = 0.0;
216   adjustment->step_increment = 0.0;
217   adjustment->page_increment = 0.0;
218   adjustment->page_size = 0.0;
219 }
220
221 static void
222 gtk_adjustment_get_property (GObject    *object,
223                              guint       prop_id,
224                              GValue     *value,
225                              GParamSpec *pspec)
226 {
227   GtkAdjustment *adjustment = GTK_ADJUSTMENT (object);
228
229   switch (prop_id)
230     {
231     case PROP_VALUE:
232       g_value_set_double (value, adjustment->value);
233       break;
234     case PROP_LOWER:
235       g_value_set_double (value, adjustment->lower);
236       break;
237     case PROP_UPPER:
238       g_value_set_double (value, adjustment->upper);
239       break;
240     case PROP_STEP_INCREMENT:
241       g_value_set_double (value, adjustment->step_increment);
242       break;
243     case PROP_PAGE_INCREMENT:
244       g_value_set_double (value, adjustment->page_increment);
245       break;
246     case PROP_PAGE_SIZE:
247       g_value_set_double (value, adjustment->page_size);
248       break;
249     default:
250       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
251       break;
252     }
253 }
254
255 static void
256 gtk_adjustment_set_property (GObject      *object,
257                              guint         prop_id,
258                              const GValue *value,
259                              GParamSpec   *pspec)
260 {
261   GtkAdjustment *adjustment = GTK_ADJUSTMENT (object);
262   gdouble double_value = g_value_get_double (value);
263
264   switch (prop_id)
265     {
266     case PROP_VALUE:
267       gtk_adjustment_set_value (adjustment, double_value);
268       break;
269     case PROP_LOWER:
270       adjustment->lower = double_value;
271       break;
272     case PROP_UPPER:
273       adjustment->upper = double_value;
274       break;
275     case PROP_STEP_INCREMENT:
276       adjustment->step_increment = double_value;
277       break;
278     case PROP_PAGE_INCREMENT:
279       adjustment->page_increment = double_value;
280       break;
281     case PROP_PAGE_SIZE:
282       adjustment->page_size = double_value;
283       break;
284     default:
285       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
286       break;
287     }
288 }
289
290 static void
291 gtk_adjustment_dispatch_properties_changed (GObject     *object,
292                                             guint        n_pspecs,
293                                             GParamSpec **pspecs)
294 {
295   gboolean changed = FALSE;
296   gint i;
297
298   G_OBJECT_CLASS (gtk_adjustment_parent_class)->dispatch_properties_changed (object, n_pspecs, pspecs);
299
300   for (i = 0; i < n_pspecs; i++)
301     switch (pspecs[i]->param_id)
302       {
303       case PROP_LOWER:
304       case PROP_UPPER:
305       case PROP_STEP_INCREMENT:
306       case PROP_PAGE_INCREMENT:
307       case PROP_PAGE_SIZE:
308         changed = TRUE;
309         break;
310       default:
311         break;
312       }
313
314   if (changed)
315     {
316       adjustment_changed_stamp++;
317       gtk_adjustment_changed (GTK_ADJUSTMENT (object));
318     }
319 }
320
321 GtkObject *
322 gtk_adjustment_new (gdouble value,
323                     gdouble lower,
324                     gdouble upper,
325                     gdouble step_increment,
326                     gdouble page_increment,
327                     gdouble page_size)
328 {
329   return g_object_new (GTK_TYPE_ADJUSTMENT,
330                        "lower", lower,
331                        "upper", upper,
332                        "step-increment", step_increment,
333                        "page-increment", page_increment,
334                        "page-size", page_size,
335                        "value", value,
336                        NULL);
337 }
338
339 /**
340  * gtk_adjustment_get_value:
341  * @adjustment: a #GtkAdjustment
342  *
343  * Gets the current value of the adjustment. See
344  * gtk_adjustment_set_value ().
345  *
346  * Return value: The current value of the adjustment.
347  **/
348 gdouble
349 gtk_adjustment_get_value (GtkAdjustment *adjustment)
350 {
351   g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0);
352
353   return adjustment->value;
354 }
355
356 void
357 gtk_adjustment_set_value (GtkAdjustment *adjustment,
358                           gdouble        value)
359 {
360   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
361
362   value = CLAMP (value, adjustment->lower, adjustment->upper - adjustment->page_size);
363
364   if (value != adjustment->value)
365     {
366       adjustment->value = value;
367
368       gtk_adjustment_value_changed (adjustment);
369     }
370 }
371
372 /**
373  * gtk_adjustment_get_lower:
374  * @adjustment: a #GtkAdjustment
375  *
376  * Retrieves the minimum value of the adjustment.
377  *
378  * Return value: The current minimum value of the adjustment.
379  *
380  * Since: 2.14
381  **/
382 gdouble
383 gtk_adjustment_get_lower (GtkAdjustment *adjustment)
384 {
385   g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0);
386
387   return adjustment->lower;
388 }
389
390 /**
391  * gtk_adjustment_set_lower:
392  * @adjustment: a #GtkAdjustment
393  * @lower: the new minimum value
394  *
395  * Sets the minimum value of the adjustment.
396  *
397  * When setting multiple adjustment properties via their individual
398  * setters, multiple "changed" signals will be emitted. However, since
399  * the emission of the "changed" signal is tied to the emission of the
400  * "GObject::notify" signals of the changed properties, it's possible
401  * to compress the "changed" signals into one by calling
402  * g_object_freeze_notify() and g_object_thaw_notify() around the
403  * calls to the individual setters.
404  *
405  * Alternatively, using a single g_object_set() for all the properties
406  * to change, or using gtk_adjustment_configure() has the same effect
407  * of compressing "changed" emissions.
408  *
409  * Since: 2.14
410  **/
411 void
412 gtk_adjustment_set_lower (GtkAdjustment *adjustment,
413                           gdouble        lower)
414 {
415   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
416
417   if (lower != adjustment->lower)
418     g_object_set (adjustment, "lower", lower, NULL);
419 }
420
421 /**
422  * gtk_adjustment_get_upper:
423  * @adjustment: a #GtkAdjustment
424  *
425  * Retrieves the maximum value of the adjustment.
426  *
427  * Return value: The current maximum value of the adjustment.
428  *
429  * Since: 2.14
430  **/
431 gdouble
432 gtk_adjustment_get_upper (GtkAdjustment *adjustment)
433 {
434   g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0);
435
436   return adjustment->upper;
437 }
438
439 /**
440  * gtk_adjustment_set_upper:
441  * @adjustment: a #GtkAdjustment
442  * @upper: the new maximum value
443  *
444  * Sets the maximum value of the adjustment.
445  *
446  * Note that values will be restricted by
447  * <literal>upper - page-size</literal> if the page-size
448  * property is nonzero.
449  *
450  * See gtk_adjustment_set_lower() about how to compress multiple
451  * emissions of the "changed" signal when setting multiple adjustment
452  * properties.
453  *
454  * Since: 2.14
455  **/
456 void
457 gtk_adjustment_set_upper (GtkAdjustment *adjustment,
458                           gdouble        upper)
459 {
460   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
461
462   if (upper != adjustment->upper)
463     g_object_set (adjustment, "upper", upper, NULL);
464 }
465
466 /**
467  * gtk_adjustment_get_step_increment:
468  * @adjustment: a #GtkAdjustment
469  *
470  * Retrieves the step increment of the adjustment.
471  *
472  * Return value: The current step increment of the adjustment.
473  *
474  * Since: 2.14
475  **/
476 gdouble
477 gtk_adjustment_get_step_increment (GtkAdjustment *adjustment)
478 {
479   g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0);
480
481   return adjustment->step_increment;
482 }
483
484 /**
485  * gtk_adjustment_set_step_increment:
486  * @adjustment: a #GtkAdjustment
487  * @step_increment: the new step increment
488  *
489  * Sets the step increment of the adjustment.
490  *
491  * See gtk_adjustment_set_lower() about how to compress multiple
492  * emissions of the "changed" signal when setting multiple adjustment
493  * properties.
494  *
495  * Since: 2.14
496  **/
497 void
498 gtk_adjustment_set_step_increment (GtkAdjustment *adjustment,
499                                    gdouble        step_increment)
500 {
501   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
502
503   if (step_increment != adjustment->step_increment)
504     g_object_set (adjustment, "step-increment", step_increment, NULL);
505 }
506
507 /**
508  * gtk_adjustment_get_page_increment:
509  * @adjustment: a #GtkAdjustment
510  *
511  * Retrieves the page increment of the adjustment.
512  *
513  * Return value: The current page increment of the adjustment.
514  *
515  * Since: 2.14
516  **/
517 gdouble
518 gtk_adjustment_get_page_increment (GtkAdjustment *adjustment)
519 {
520   g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0);
521
522   return adjustment->page_increment;
523 }
524
525 /**
526  * gtk_adjustment_set_page_increment:
527  * @adjustment: a #GtkAdjustment
528  * @page_increment: the new page increment
529  *
530  * Sets the page increment of the adjustment.
531  *
532  * See gtk_adjustment_set_lower() about how to compress multiple
533  * emissions of the "changed" signal when setting multiple adjustment
534  * properties.
535  *
536  * Since: 2.14
537  **/
538 void
539 gtk_adjustment_set_page_increment (GtkAdjustment *adjustment,
540                                    gdouble        page_increment)
541 {
542   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
543
544   if (page_increment != adjustment->page_increment)
545     g_object_set (adjustment, "page-increment", page_increment, NULL);
546 }
547
548 /**
549  * gtk_adjustment_get_page_size:
550  * @adjustment: a #GtkAdjustment
551  *
552  * Retrieves the page size of the adjustment.
553  *
554  * Return value: The current page size of the adjustment.
555  *
556  * Since: 2.14
557  **/
558 gdouble
559 gtk_adjustment_get_page_size (GtkAdjustment *adjustment)
560 {
561   g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0);
562
563   return adjustment->page_size;
564 }
565
566 /**
567  * gtk_adjustment_set_page_size:
568  * @adjustment: a #GtkAdjustment
569  * @page_size: the new page size
570  *
571  * Sets the page size of the adjustment.
572  *
573  * See gtk_adjustment_set_lower() about how to compress multiple
574  * emissions of the "changed" signal when setting multiple adjustment
575  * properties.
576  *
577  * Since: 2.14
578  **/
579 void
580 gtk_adjustment_set_page_size (GtkAdjustment *adjustment,
581                               gdouble        page_size)
582 {
583   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
584
585   if (page_size != adjustment->page_size)
586     g_object_set (adjustment, "page-size", page_size, NULL);
587 }
588
589 /**
590  * gtk_adjustment_configure:
591  * @adjustment: a #GtkAdjustment
592  * @value: the new value
593  * @lower: the new minimum value
594  * @upper: the new maximum value
595  * @step_increment: the new step increment
596  * @page_increment: the new page increment
597  * @page_size: the new page size
598  *
599  * Sets all properties of the adjustment at once.
600  *
601  * Use this function to avoid multiple emissions of the "changed"
602  * signal. See gtk_adjustment_set_lower() for an alternative way
603  * of compressing multiple emissions of "changed" into one.
604  *
605  * Since: 2.14
606  **/
607 void
608 gtk_adjustment_configure (GtkAdjustment *adjustment,
609                           gdouble        value,
610                           gdouble        lower,
611                           gdouble        upper,
612                           gdouble        step_increment,
613                           gdouble        page_increment,
614                           gdouble        page_size)
615 {
616   gboolean value_changed = FALSE;
617   guint64 old_stamp = adjustment_changed_stamp;
618
619   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
620
621   g_object_freeze_notify (G_OBJECT (adjustment));
622
623   g_object_set (adjustment,
624                 "lower", lower,
625                 "upper", upper,
626                 "step-increment", step_increment,
627                 "page-increment", page_increment,
628                 "page-size", page_size,
629                 NULL);
630
631   value = CLAMP (value, lower, upper - page_size);
632
633   if (value != adjustment->value)
634     {
635       /* set value manually to make sure "changed" is emitted with the
636        * new value in place and is emitted before "value-changed"
637        */
638       adjustment->value = value;
639       value_changed = TRUE;
640     }
641
642   g_object_thaw_notify (G_OBJECT (adjustment));
643
644   if (old_stamp == adjustment_changed_stamp)
645     gtk_adjustment_changed (adjustment); /* force emission before ::value-changed */
646
647   if (value_changed)
648     gtk_adjustment_value_changed (adjustment);
649 }
650
651 void
652 gtk_adjustment_changed (GtkAdjustment *adjustment)
653 {
654   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
655
656   g_signal_emit (adjustment, adjustment_signals[CHANGED], 0);
657 }
658
659 void
660 gtk_adjustment_value_changed (GtkAdjustment *adjustment)
661 {
662   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
663
664   g_signal_emit (adjustment, adjustment_signals[VALUE_CHANGED], 0);
665   g_object_notify (G_OBJECT (adjustment), "value");
666 }
667
668 void
669 gtk_adjustment_clamp_page (GtkAdjustment *adjustment,
670                            gdouble        lower,
671                            gdouble        upper)
672 {
673   gboolean need_emission;
674
675   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
676
677   lower = CLAMP (lower, adjustment->lower, adjustment->upper);
678   upper = CLAMP (upper, adjustment->lower, adjustment->upper);
679
680   need_emission = FALSE;
681
682   if (adjustment->value + adjustment->page_size < upper)
683     {
684       adjustment->value = upper - adjustment->page_size;
685       need_emission = TRUE;
686     }
687   if (adjustment->value > lower)
688     {
689       adjustment->value = lower;
690       need_emission = TRUE;
691     }
692
693   if (need_emission)
694     gtk_adjustment_value_changed (adjustment);
695 }
696
697 #define __GTK_ADJUSTMENT_C__
698 #include "gtkaliasdef.c"