Set the minimum-extent property from the size_allocate handler of the sheet
[pspp-builds.git] / lib / gtksheet / psppire-axis.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008  Free Software Foundation
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program 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
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18 #include <string.h>
19 #include <stdlib.h>
20
21 #include <libpspp/tower.h>
22 #include <libpspp/pool.h>
23 #include "psppire-axis.h"
24 #include <gtk/gtk.h>
25
26
27 /* --- prototypes --- */
28 static void psppire_axis_class_init (PsppireAxisClass   *class);
29 static void psppire_axis_init   (PsppireAxis            *axis);
30 static void psppire_axis_finalize   (GObject            *object);
31
32
33 /* --- variables --- */
34 static GObjectClass     *parent_class = NULL;
35
36 /* --- functions --- */
37 /**
38  * psppire_axis_get_type:
39  * @returns: the type ID for accelerator groups.
40  */
41 GType
42 psppire_axis_get_type (void)
43 {
44   static GType object_type = 0;
45
46   if (!object_type)
47     {
48       static const GTypeInfo object_info = {
49         sizeof (PsppireAxisClass),
50         (GBaseInitFunc) NULL,
51         (GBaseFinalizeFunc) NULL,
52         (GClassInitFunc) psppire_axis_class_init,
53         NULL,   /* class_finalize */
54         NULL,   /* class_data */
55         sizeof (PsppireAxis),
56         0,      /* n_preallocs */
57         (GInstanceInitFunc) psppire_axis_init,
58       };
59
60       object_type = g_type_register_static (G_TYPE_OBJECT,
61                                             "PsppireAxis",
62                                             &object_info, 0);
63     }
64
65   return object_type;
66 }
67
68 enum
69   {
70     PROP_0,
71     PROP_MIN_EXTENT,
72     PROP_DEFAULT_SIZE
73   };
74
75
76 static void
77 psppire_axis_get_property (GObject         *object,
78                            guint            prop_id,
79                            GValue          *value,
80                            GParamSpec      *pspec)
81 {
82   PsppireAxis *axis = PSPPIRE_AXIS (object);
83
84   switch (prop_id)
85     {
86     case PROP_MIN_EXTENT:
87       g_value_set_long (value, axis->min_extent);
88       break;
89     case PROP_DEFAULT_SIZE:
90       g_value_set_int (value, axis->default_size);
91       break;
92     default:
93       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
94       break;
95     };
96 }
97
98
99 static void
100 psppire_axis_set_property (GObject         *object,
101                            guint            prop_id,
102                            const GValue    *value,
103                            GParamSpec      *pspec)
104 {
105   PsppireAxis *axis = PSPPIRE_AXIS (object);
106
107   switch (prop_id)
108     {
109     case PROP_MIN_EXTENT:
110       axis->min_extent = g_value_get_long (value);
111       break;
112     case PROP_DEFAULT_SIZE:
113       axis->default_size = g_value_get_int (value);
114       break;
115     default:
116       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
117       break;
118     };
119 }
120
121 static void
122 psppire_axis_class_init (PsppireAxisClass *class)
123 {
124   GObjectClass *object_class = G_OBJECT_CLASS (class);
125   GParamSpec *min_extent_spec;
126   GParamSpec *default_size_spec;
127
128   object_class->set_property = psppire_axis_set_property;
129   object_class->get_property = psppire_axis_get_property;
130
131   min_extent_spec =
132     g_param_spec_long ("minimum-extent",
133                        "Minimum Extent",
134                        "The smallest extent to which the axis will provide units (typically set to the height/width of the associated widget)",
135                        0, G_MAXLONG,
136                        800,
137                        G_PARAM_WRITABLE | G_PARAM_READABLE );
138
139   g_object_class_install_property (object_class,
140                                    PROP_MIN_EXTENT,
141                                    min_extent_spec);
142
143
144   default_size_spec =
145     g_param_spec_pointer ("default-size",
146                           "Default Size",
147                           "The size given to units which haven't been explicity inserted",
148                           G_PARAM_WRITABLE | G_PARAM_READABLE );
149
150
151   g_object_class_install_property (object_class,
152                                    PROP_DEFAULT_SIZE,
153                                    default_size_spec);
154
155   parent_class = g_type_class_peek_parent (class);
156
157   object_class->finalize = psppire_axis_finalize;
158 }
159
160
161 static void
162 psppire_axis_init (PsppireAxis *axis)
163 {
164   axis->min_extent = 800;
165   axis->default_size = 75;
166   axis->pool = NULL;
167   psppire_axis_clear (axis);
168 }
169
170
171 static void
172 psppire_axis_finalize (GObject *object)
173 {
174   PsppireAxis *a = PSPPIRE_AXIS (object);
175   pool_destroy (a->pool);
176   G_OBJECT_CLASS (parent_class)->finalize (object);
177 }
178
179 /**
180  * psppire_axis_new:
181  * @returns: a new #PsppireAxis object
182  *
183  * Creates a new #PsppireAxis.
184  */
185 PsppireAxis*
186 psppire_axis_new (void)
187 {
188   return g_object_new (G_TYPE_PSPPIRE_AXIS, NULL);
189 }
190
191
192 gint
193 psppire_axis_unit_size (PsppireAxis *a, gint unit)
194 {
195   const struct tower_node *node;
196   if  (unit >= tower_count (&a->tower))
197     return a->default_size;
198
199   node = tower_get (&a->tower, unit);
200
201   return tower_node_get_size (node);
202 }
203
204 gint
205 psppire_axis_unit_count (PsppireAxis *a)
206 {
207   glong padding = 0;
208
209   if ( tower_height (&a->tower) < a->min_extent )
210     padding = (a->min_extent - tower_height (&a->tower))
211       / a->default_size;
212
213   return tower_count (&a->tower) + padding;
214 }
215
216
217 /* Return the starting pixel of UNIT */
218 glong
219 psppire_axis_pixel_start (PsppireAxis *a, gint unit)
220 {
221   const struct tower_node *node;
222
223   if ( unit >= tower_count (&a->tower))
224     {
225       return  tower_height (&a->tower) +
226         (unit - tower_count (&a->tower)) * a->default_size;
227     }
228
229   node = tower_get (&a->tower, unit);
230   return  tower_node_get_level (node);
231 }
232
233
234 /* Return the unit covered by PIXEL */
235 gint
236 psppire_axis_get_unit_at_pixel (PsppireAxis *a, glong pixel)
237 {
238   const struct tower_node *node;
239   unsigned long int node_start;
240
241   if (pixel >= tower_height (&a->tower))
242     {
243       glong extra = pixel - tower_height (&a->tower);
244
245       if ( extra > a->min_extent - tower_height (&a->tower))
246         extra = a->min_extent - tower_height (&a->tower);
247
248       return tower_count (&a->tower) - 1 + extra / a->default_size;
249     }
250
251   node = tower_lookup (&a->tower, pixel, &node_start);
252
253   return  tower_node_get_index (node);
254 }
255
256 void
257 psppire_axis_append (PsppireAxis *a, gint size)
258 {
259   struct tower_node *new = pool_malloc (a->pool, sizeof *new);
260
261   tower_insert (&a->tower, size, new, NULL);
262 }
263
264
265
266 /* Insert a new unit of size SIZE before position POSN */
267 void
268 psppire_axis_insert (PsppireAxis *a, gint size, gint posn)
269 {
270   struct tower_node *new = pool_malloc (a->pool, sizeof *new);
271
272   struct tower_node *before = NULL;
273
274   if ( posn != tower_count (&a->tower))
275     before = tower_get (&a->tower, posn);
276
277   tower_insert (&a->tower, size, new, before);
278 }
279
280
281 void
282 psppire_axis_remove (PsppireAxis *a, gint posn)
283 {
284   struct tower_node *node = tower_get (&a->tower, posn);
285
286   tower_delete (&a->tower, node);
287
288   pool_free (a->pool, node);
289 }
290
291
292 void
293 psppire_axis_resize_unit (PsppireAxis *a, gint size, gint posn)
294 {
295   struct tower_node *node = tower_get (&a->tower, posn);
296
297   tower_resize (&a->tower, node, size);
298 }
299
300
301 void
302 psppire_axis_clear (PsppireAxis *a)
303 {
304   pool_destroy (a->pool);
305   a->pool = pool_create ();
306   tower_init (&a->tower);
307 }
308
309