Fixed minor bug getting the extent of the last unit
[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_pointer ("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                           G_PARAM_WRITABLE | G_PARAM_READABLE );
136
137   g_object_class_install_property (object_class,
138                                    PROP_MIN_EXTENT,
139                                    min_extent_spec);
140
141
142   default_size_spec =
143     g_param_spec_pointer ("default-size",
144                           "Default Size",
145                           "The size given to units which haven't been explicity inserted",
146                           G_PARAM_WRITABLE | G_PARAM_READABLE );
147
148
149   g_object_class_install_property (object_class,
150                                    PROP_DEFAULT_SIZE,
151                                    default_size_spec);
152
153   parent_class = g_type_class_peek_parent (class);
154
155   object_class->finalize = psppire_axis_finalize;
156 }
157
158
159 static void
160 psppire_axis_init (PsppireAxis *axis)
161 {
162   axis->min_extent = 800;
163   axis->default_size = 75;
164   axis->pool = NULL;
165   psppire_axis_clear (axis);
166 }
167
168
169 static void
170 psppire_axis_finalize (GObject *object)
171 {
172   PsppireAxis *a = PSPPIRE_AXIS (object);
173   pool_destroy (a->pool);
174   G_OBJECT_CLASS (parent_class)->finalize (object);
175 }
176
177 /**
178  * psppire_axis_new:
179  * @returns: a new #PsppireAxis object
180  *
181  * Creates a new #PsppireAxis.
182  */
183 PsppireAxis*
184 psppire_axis_new (void)
185 {
186   return g_object_new (G_TYPE_PSPPIRE_AXIS, NULL);
187 }
188
189
190 gint
191 psppire_axis_unit_size (PsppireAxis *a, gint unit)
192 {
193   const struct tower_node *node;
194   if  (unit >= tower_count (&a->tower))
195     return a->default_size;
196
197   node = tower_get (&a->tower, unit);
198
199   return tower_node_get_size (node);
200 }
201
202 gint
203 psppire_axis_unit_count (PsppireAxis *a)
204 {
205   glong padding = 0;
206
207   if ( tower_height (&a->tower) < a->min_extent )
208     padding = (a->min_extent - tower_height (&a->tower))
209       / a->default_size;
210
211   return tower_count (&a->tower) + padding;
212 }
213
214
215 /* Return the starting pixel of UNIT */
216 glong
217 psppire_axis_pixel_start (PsppireAxis *a, gint unit)
218 {
219   const struct tower_node *node;
220
221   if ( unit >= tower_count (&a->tower))
222     {
223       return  tower_height (&a->tower) +
224         (unit - tower_count (&a->tower)) * a->default_size;
225     }
226
227   node = tower_get (&a->tower, unit);
228   return  tower_node_get_level (node);
229 }
230
231
232 /* Return the unit covered by PIXEL */
233 gint
234 psppire_axis_get_unit_at_pixel (PsppireAxis *a, glong pixel)
235 {
236   const struct tower_node *node;
237   unsigned long int node_start;
238
239   if (pixel >= tower_height (&a->tower))
240     {
241       glong extra = pixel - tower_height (&a->tower);
242
243       if ( extra > a->min_extent - tower_height (&a->tower))
244         extra = a->min_extent - tower_height (&a->tower);
245
246       return tower_count (&a->tower) - 1 + extra / a->default_size;
247     }
248
249   node = tower_lookup (&a->tower, pixel, &node_start);
250
251   return  tower_node_get_index (node);
252 }
253
254 void
255 psppire_axis_append (PsppireAxis *a, gint size)
256 {
257   struct tower_node *new = pool_malloc (a->pool, sizeof *new);
258
259   tower_insert (&a->tower, size, new, NULL);
260 }
261
262
263
264 /* Insert a new unit of size SIZE before position POSN */
265 void
266 psppire_axis_insert (PsppireAxis *a, gint size, gint posn)
267 {
268   struct tower_node *new = pool_malloc (a->pool, sizeof *new);
269
270   struct tower_node *before = NULL;
271
272   if ( posn != tower_count (&a->tower))
273     before = tower_get (&a->tower, posn);
274
275   tower_insert (&a->tower, size, new, before);
276 }
277
278
279 void
280 psppire_axis_remove (PsppireAxis *a, gint posn)
281 {
282   struct tower_node *node = tower_get (&a->tower, posn);
283
284   tower_delete (&a->tower, node);
285
286   pool_free (a->pool, node);
287 }
288
289
290 void
291 psppire_axis_resize_unit (PsppireAxis *a, gint size, gint posn)
292 {
293   struct tower_node *node = tower_get (&a->tower, posn);
294
295   tower_resize (&a->tower, node, size);
296 }
297
298
299 void
300 psppire_axis_clear (PsppireAxis *a)
301 {
302   pool_destroy (a->pool);
303   a->pool = pool_create ();
304   tower_init (&a->tower);
305 }
306
307