]> Pileus Git - ~andy/gtk/blob - gtk/gtkadjustment.c
Move classes that currently derive from GtkObject to GInitiallyUnowned
[~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
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, G_TYPE_INITIALLY_UNOWNED)
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 GObject *
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   /* don't use CLAMP() so we don't end up below lower if upper - page_size
363    * is smaller than lower
364    */
365   value = MIN (value, adjustment->upper - adjustment->page_size);
366   value = MAX (value, adjustment->lower);
367
368   if (value != adjustment->value)
369     {
370       adjustment->value = value;
371
372       gtk_adjustment_value_changed (adjustment);
373     }
374 }
375
376 /**
377  * gtk_adjustment_get_lower:
378  * @adjustment: a #GtkAdjustment
379  *
380  * Retrieves the minimum value of the adjustment.
381  *
382  * Return value: The current minimum value of the adjustment.
383  *
384  * Since: 2.14
385  **/
386 gdouble
387 gtk_adjustment_get_lower (GtkAdjustment *adjustment)
388 {
389   g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0);
390
391   return adjustment->lower;
392 }
393
394 /**
395  * gtk_adjustment_set_lower:
396  * @adjustment: a #GtkAdjustment
397  * @lower: the new minimum value
398  *
399  * Sets the minimum value of the adjustment.
400  *
401  * When setting multiple adjustment properties via their individual
402  * setters, multiple "changed" signals will be emitted. However, since
403  * the emission of the "changed" signal is tied to the emission of the
404  * "GObject::notify" signals of the changed properties, it's possible
405  * to compress the "changed" signals into one by calling
406  * g_object_freeze_notify() and g_object_thaw_notify() around the
407  * calls to the individual setters.
408  *
409  * Alternatively, using a single g_object_set() for all the properties
410  * to change, or using gtk_adjustment_configure() has the same effect
411  * of compressing "changed" emissions.
412  *
413  * Since: 2.14
414  **/
415 void
416 gtk_adjustment_set_lower (GtkAdjustment *adjustment,
417                           gdouble        lower)
418 {
419   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
420
421   if (lower != adjustment->lower)
422     g_object_set (adjustment, "lower", lower, NULL);
423 }
424
425 /**
426  * gtk_adjustment_get_upper:
427  * @adjustment: a #GtkAdjustment
428  *
429  * Retrieves the maximum value of the adjustment.
430  *
431  * Return value: The current maximum value of the adjustment.
432  *
433  * Since: 2.14
434  **/
435 gdouble
436 gtk_adjustment_get_upper (GtkAdjustment *adjustment)
437 {
438   g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0);
439
440   return adjustment->upper;
441 }
442
443 /**
444  * gtk_adjustment_set_upper:
445  * @adjustment: a #GtkAdjustment
446  * @upper: the new maximum value
447  *
448  * Sets the maximum value of the adjustment.
449  *
450  * Note that values will be restricted by
451  * <literal>upper - page-size</literal> if the page-size
452  * property is nonzero.
453  *
454  * See gtk_adjustment_set_lower() about how to compress multiple
455  * emissions of the "changed" signal when setting multiple adjustment
456  * properties.
457  *
458  * Since: 2.14
459  **/
460 void
461 gtk_adjustment_set_upper (GtkAdjustment *adjustment,
462                           gdouble        upper)
463 {
464   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
465
466   if (upper != adjustment->upper)
467     g_object_set (adjustment, "upper", upper, NULL);
468 }
469
470 /**
471  * gtk_adjustment_get_step_increment:
472  * @adjustment: a #GtkAdjustment
473  *
474  * Retrieves the step increment of the adjustment.
475  *
476  * Return value: The current step increment of the adjustment.
477  *
478  * Since: 2.14
479  **/
480 gdouble
481 gtk_adjustment_get_step_increment (GtkAdjustment *adjustment)
482 {
483   g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0);
484
485   return adjustment->step_increment;
486 }
487
488 /**
489  * gtk_adjustment_set_step_increment:
490  * @adjustment: a #GtkAdjustment
491  * @step_increment: the new step increment
492  *
493  * Sets the step increment of the adjustment.
494  *
495  * See gtk_adjustment_set_lower() about how to compress multiple
496  * emissions of the "changed" signal when setting multiple adjustment
497  * properties.
498  *
499  * Since: 2.14
500  **/
501 void
502 gtk_adjustment_set_step_increment (GtkAdjustment *adjustment,
503                                    gdouble        step_increment)
504 {
505   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
506
507   if (step_increment != adjustment->step_increment)
508     g_object_set (adjustment, "step-increment", step_increment, NULL);
509 }
510
511 /**
512  * gtk_adjustment_get_page_increment:
513  * @adjustment: a #GtkAdjustment
514  *
515  * Retrieves the page increment of the adjustment.
516  *
517  * Return value: The current page increment of the adjustment.
518  *
519  * Since: 2.14
520  **/
521 gdouble
522 gtk_adjustment_get_page_increment (GtkAdjustment *adjustment)
523 {
524   g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0);
525
526   return adjustment->page_increment;
527 }
528
529 /**
530  * gtk_adjustment_set_page_increment:
531  * @adjustment: a #GtkAdjustment
532  * @page_increment: the new page increment
533  *
534  * Sets the page increment of the adjustment.
535  *
536  * See gtk_adjustment_set_lower() about how to compress multiple
537  * emissions of the "changed" signal when setting multiple adjustment
538  * properties.
539  *
540  * Since: 2.14
541  **/
542 void
543 gtk_adjustment_set_page_increment (GtkAdjustment *adjustment,
544                                    gdouble        page_increment)
545 {
546   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
547
548   if (page_increment != adjustment->page_increment)
549     g_object_set (adjustment, "page-increment", page_increment, NULL);
550 }
551
552 /**
553  * gtk_adjustment_get_page_size:
554  * @adjustment: a #GtkAdjustment
555  *
556  * Retrieves the page size of the adjustment.
557  *
558  * Return value: The current page size of the adjustment.
559  *
560  * Since: 2.14
561  **/
562 gdouble
563 gtk_adjustment_get_page_size (GtkAdjustment *adjustment)
564 {
565   g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), 0.0);
566
567   return adjustment->page_size;
568 }
569
570 /**
571  * gtk_adjustment_set_page_size:
572  * @adjustment: a #GtkAdjustment
573  * @page_size: the new page size
574  *
575  * Sets the page size of the adjustment.
576  *
577  * See gtk_adjustment_set_lower() about how to compress multiple
578  * emissions of the "changed" signal when setting multiple adjustment
579  * properties.
580  *
581  * Since: 2.14
582  **/
583 void
584 gtk_adjustment_set_page_size (GtkAdjustment *adjustment,
585                               gdouble        page_size)
586 {
587   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
588
589   if (page_size != adjustment->page_size)
590     g_object_set (adjustment, "page-size", page_size, NULL);
591 }
592
593 /**
594  * gtk_adjustment_configure:
595  * @adjustment: a #GtkAdjustment
596  * @value: the new value
597  * @lower: the new minimum value
598  * @upper: the new maximum value
599  * @step_increment: the new step increment
600  * @page_increment: the new page increment
601  * @page_size: the new page size
602  *
603  * Sets all properties of the adjustment at once.
604  *
605  * Use this function to avoid multiple emissions of the "changed"
606  * signal. See gtk_adjustment_set_lower() for an alternative way
607  * of compressing multiple emissions of "changed" into one.
608  *
609  * Since: 2.14
610  **/
611 void
612 gtk_adjustment_configure (GtkAdjustment *adjustment,
613                           gdouble        value,
614                           gdouble        lower,
615                           gdouble        upper,
616                           gdouble        step_increment,
617                           gdouble        page_increment,
618                           gdouble        page_size)
619 {
620   gboolean value_changed = FALSE;
621   guint64 old_stamp = adjustment_changed_stamp;
622
623   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
624
625   g_object_freeze_notify (G_OBJECT (adjustment));
626
627   g_object_set (adjustment,
628                 "lower", lower,
629                 "upper", upper,
630                 "step-increment", step_increment,
631                 "page-increment", page_increment,
632                 "page-size", page_size,
633                 NULL);
634
635   /* don't use CLAMP() so we don't end up below lower if upper - page_size
636    * is smaller than lower
637    */
638   value = MIN (value, upper - page_size);
639   value = MAX (value, lower);
640
641   if (value != adjustment->value)
642     {
643       /* set value manually to make sure "changed" is emitted with the
644        * new value in place and is emitted before "value-changed"
645        */
646       adjustment->value = value;
647       value_changed = TRUE;
648     }
649
650   g_object_thaw_notify (G_OBJECT (adjustment));
651
652   if (old_stamp == adjustment_changed_stamp)
653     gtk_adjustment_changed (adjustment); /* force emission before ::value-changed */
654
655   if (value_changed)
656     gtk_adjustment_value_changed (adjustment);
657 }
658
659 void
660 gtk_adjustment_changed (GtkAdjustment *adjustment)
661 {
662   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
663
664   g_signal_emit (adjustment, adjustment_signals[CHANGED], 0);
665 }
666
667 void
668 gtk_adjustment_value_changed (GtkAdjustment *adjustment)
669 {
670   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
671
672   g_signal_emit (adjustment, adjustment_signals[VALUE_CHANGED], 0);
673   g_object_notify (G_OBJECT (adjustment), "value");
674 }
675
676 void
677 gtk_adjustment_clamp_page (GtkAdjustment *adjustment,
678                            gdouble        lower,
679                            gdouble        upper)
680 {
681   gboolean need_emission;
682
683   g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
684
685   lower = CLAMP (lower, adjustment->lower, adjustment->upper);
686   upper = CLAMP (upper, adjustment->lower, adjustment->upper);
687
688   need_emission = FALSE;
689
690   if (adjustment->value + adjustment->page_size < upper)
691     {
692       adjustment->value = upper - adjustment->page_size;
693       need_emission = TRUE;
694     }
695   if (adjustment->value > lower)
696     {
697       adjustment->value = lower;
698       need_emission = TRUE;
699     }
700
701   if (need_emission)
702     gtk_adjustment_value_changed (adjustment);
703 }