pspp: Make a signal that indicates a bug re-raise that signal to exit.
[pspp] / src / ui / gui / sheet / psppire-axis.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2009, 2010  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 <ui/gui/psppire-marshal.h>
22 #include <libpspp/tower.h>
23 #include <libpspp/pool.h>
24 #include "psppire-axis.h"
25 #include <math.h>
26 #include <libpspp/misc.h>
27
28
29 /* Signals */
30 enum
31   {
32     RESIZE_UNIT,
33     n_signals
34   };
35
36 static guint signals[n_signals] ;
37
38 /* --- prototypes --- */
39 static void psppire_axis_class_init (PsppireAxisClass   *class);
40 static void psppire_axis_init   (PsppireAxis            *axis);
41 static void psppire_axis_finalize   (GObject            *object);
42
43
44 /* --- variables --- */
45 static GObjectClass     *parent_class = NULL;
46
47
48 struct axis_node
49 {
50   struct tower_node pixel_node;
51   struct tower_node unit_node;
52 };
53
54 void
55 psppire_axis_dump (const PsppireAxis *a)
56 {
57   struct tower_node *n = tower_first (&a->unit_tower);
58
59   g_debug ("Axis %p", a);
60   while (n)
61     {
62       const struct axis_node *an = tower_data (n, struct axis_node, unit_node);
63       const struct tower_node *pn = &an->pixel_node;
64       g_debug ("%ld units of height %g",
65                n->size, pn->size / (gdouble) n->size);
66
67       n =  tower_next (&a->unit_tower, n);
68     }
69   g_debug ("\n");
70 }
71
72 /* Increment the size of every unit by INC.
73    Note that INC is signed. So if INC is negative,
74    then size will end up smaller.
75 */
76 static void
77 axis_increment (PsppireAxis *axis, gint inc)
78 {
79   struct tower_node *n = tower_first (&axis->pixel_tower);
80
81   while (n)
82     {
83       struct axis_node *an = tower_data (n, struct axis_node, pixel_node);
84       struct tower_node *pn = &an->pixel_node;
85       const gint existing_size = tower_node_get_size (pn);
86
87       tower_resize (&axis->pixel_tower, pn, existing_size + inc *
88                     tower_node_get_size (&an->unit_node));
89
90       n = tower_next (&axis->pixel_tower, n);
91     }
92 }
93
94
95 /* Return the unit covered by PIXEL */
96 gint
97 psppire_axis_unit_at_pixel (const PsppireAxis *a, glong pixel)
98 {
99   unsigned long int start;
100   struct tower_node *n;
101   struct axis_node *an;
102   gdouble fraction;
103
104   glong size = tower_height (&a->pixel_tower);
105
106   g_return_val_if_fail (pixel >= 0, -1);
107
108   if (pixel >= size)
109     {
110       gint n_items = tower_height (&a->unit_tower);
111       glong extra = pixel - size;
112
113       return n_items - 1 + DIV_RND_UP (extra,  a->default_size);
114     }
115
116
117   n = tower_lookup (&a->pixel_tower, pixel, &start);
118   an = tower_data (n, struct axis_node, pixel_node);
119
120   fraction = (pixel - start) / (gdouble) tower_node_get_size (&an->pixel_node);
121
122   return  tower_node_get_level (&an->unit_node)
123     + fraction * tower_node_get_size (&an->unit_node);
124 }
125
126
127 gint
128 psppire_axis_unit_count (const PsppireAxis *a)
129 {
130   glong filler = 0;
131   glong actual_size;
132
133   actual_size = tower_height (&a->pixel_tower);
134
135   if ( actual_size < a->min_extent )
136     filler = DIV_RND_UP (a->min_extent - actual_size, a->default_size);
137
138   return tower_height (&a->unit_tower) + filler;
139 }
140
141
142 /* Return the starting pixel of UNIT */
143 glong
144 psppire_axis_start_pixel (const PsppireAxis *a, gint unit)
145 {
146   gdouble fraction;
147   struct tower_node *n ;
148   struct axis_node *an;
149
150   unsigned long int start;
151
152   gint the_count, size ;
153
154   the_count =  tower_height (&a->unit_tower);
155   size = tower_height (&a->pixel_tower);
156
157   if ( unit >= the_count)
158     {
159       return  size + (unit - the_count) * a->default_size;
160     }
161
162   if ( unit < 0)
163     return -1;
164
165   if ( unit >= tower_height (&a->unit_tower))
166     return -1;
167
168   n = tower_lookup (&a->unit_tower, unit, &start);
169
170   an = tower_data (n, struct axis_node, unit_node);
171
172   fraction = (unit - start) / (gdouble) tower_node_get_size (&an->unit_node);
173
174   return  tower_node_get_level (&an->pixel_node) +
175     rint (fraction * tower_node_get_size (&an->pixel_node));
176 }
177
178 gint
179 psppire_axis_unit_size (const PsppireAxis *axis, gint unit)
180 {
181   struct tower_node *n ;
182   struct axis_node *an;
183
184   unsigned long int start;
185
186   if  (unit >= tower_height (&axis->unit_tower))
187     return axis->default_size;
188
189   if ( unit < 0)
190     return 0;
191
192   if ( unit >= tower_height (&axis->unit_tower))
193     return 0;
194
195   n = tower_lookup (&axis->unit_tower, unit, &start);
196
197   an = tower_data (n, struct axis_node, unit_node);
198
199   return rint (tower_node_get_size (&an->pixel_node)
200                / (gdouble) tower_node_get_size (&an->unit_node));
201 }
202
203
204
205 /* --- functions --- */
206 /**
207  * psppire_axis_get_type:
208  * @returns: the type ID for accelerator groups.
209  */
210 GType
211 psppire_axis_get_type (void)
212 {
213   static GType object_type = 0;
214
215   if (!object_type)
216     {
217       static const GTypeInfo object_info = {
218         sizeof (PsppireAxisClass),
219         (GBaseInitFunc) NULL,
220         (GBaseFinalizeFunc) NULL,
221         (GClassInitFunc) psppire_axis_class_init,
222         NULL,   /* class_finalize */
223         NULL,   /* class_data */
224         sizeof (PsppireAxis),
225         0,      /* n_preallocs */
226         (GInstanceInitFunc) psppire_axis_init,
227       };
228
229       object_type = g_type_register_static (G_TYPE_OBJECT,
230                                             "PsppireAxis",
231                                             &object_info, 0);
232
233     }
234
235   return object_type;
236 }
237
238 enum
239   {
240     PROP_0,
241     PROP_MIN_EXTENT,
242     PROP_DEFAULT_SIZE,
243     PROP_PADDING
244   };
245
246
247 static void
248 psppire_axis_get_property (GObject         *object,
249                            guint            prop_id,
250                            GValue          *value,
251                            GParamSpec      *pspec)
252 {
253   PsppireAxis *axis = PSPPIRE_AXIS (object);
254
255   switch (prop_id)
256     {
257     case PROP_PADDING:
258       g_value_set_int (value, axis->padding);
259       break;
260     case PROP_MIN_EXTENT:
261       g_value_set_long (value, axis->min_extent);
262       break;
263     case PROP_DEFAULT_SIZE:
264       g_value_set_int (value, axis->default_size);
265       break;
266     default:
267       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
268       break;
269     };
270 }
271
272
273 static void
274 psppire_axis_set_property (GObject         *object,
275                            guint            prop_id,
276                            const GValue    *value,
277                            GParamSpec      *pspec)
278 {
279   PsppireAxis *axis = PSPPIRE_AXIS (object);
280
281   switch (prop_id)
282     {
283     case PROP_PADDING:
284       {
285         const gint old_value = axis->padding;
286         axis->padding = g_value_get_int (value);
287         axis_increment (axis, axis->padding - old_value);
288       }
289       break;
290     case PROP_MIN_EXTENT:
291       axis->min_extent = g_value_get_long (value);
292       break;
293     case PROP_DEFAULT_SIZE:
294       axis->default_size = g_value_get_int (value);
295       break;
296     default:
297       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
298       break;
299     };
300 }
301
302
303 static void
304 psppire_axis_class_init (PsppireAxisClass *class)
305 {
306   GObjectClass *object_class = G_OBJECT_CLASS (class);
307
308   GParamSpec *padding_spec;
309   GParamSpec *min_extent_spec;
310   GParamSpec *default_size_spec;
311
312   parent_class = g_type_class_peek_parent (class);
313
314   object_class->set_property = psppire_axis_set_property;
315   object_class->get_property = psppire_axis_get_property;
316
317
318   min_extent_spec =
319     g_param_spec_long ("minimum-extent",
320                        "Minimum Extent",
321                        "The smallest extent to which the axis will provide units (typically set to the height/width of the associated widget).",
322                        0, G_MAXLONG,
323                        0,
324                        G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE );
325
326   g_object_class_install_property (object_class,
327                                    PROP_MIN_EXTENT,
328                                    min_extent_spec);
329
330
331   default_size_spec =
332     g_param_spec_int ("default-size",
333                       "Default Size",
334                       "The size given to units which haven't been explicity inserted",
335                       0, G_MAXINT,
336                       25,
337                       G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE );
338
339
340   g_object_class_install_property (object_class,
341                                    PROP_DEFAULT_SIZE,
342                                    default_size_spec);
343
344   padding_spec =
345     g_param_spec_int ("padding",
346                       "Padding",
347                       "Extra space implicitly added to each unit",
348                       0, G_MAXINT,
349                       0,
350                       G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE );
351
352
353   g_object_class_install_property (object_class,
354                                    PROP_PADDING,
355                                    padding_spec);
356
357
358
359   signals[RESIZE_UNIT] =
360     g_signal_new ("resize-unit",
361                   G_TYPE_FROM_CLASS (object_class),
362                   G_SIGNAL_RUN_LAST,
363                   0,
364                   NULL, NULL,
365                   psppire_marshal_VOID__INT_LONG,
366                   G_TYPE_NONE,
367                   2,
368                   G_TYPE_INT,
369                   G_TYPE_LONG
370                   );
371
372
373   object_class->finalize = psppire_axis_finalize;
374 }
375
376
377 static void
378 psppire_axis_init (PsppireAxis *axis)
379 {
380   tower_init (&axis->pixel_tower);
381   tower_init (&axis->unit_tower);
382
383   axis->pool = pool_create ();
384   axis->padding = 0;
385 }
386
387
388 static void
389 psppire_axis_finalize (GObject *object)
390 {
391   PsppireAxis *a = PSPPIRE_AXIS (object);
392   pool_destroy (a->pool);
393
394   G_OBJECT_CLASS (parent_class)->finalize (object);
395 }
396
397 /**
398  * psppire_axis_new:
399  * @returns: a new #PsppireAxis object
400  *
401  * Creates a new #PsppireAxis.
402  */
403 PsppireAxis*
404 psppire_axis_new (void)
405 {
406   return g_object_new (G_TYPE_PSPPIRE_AXIS, NULL);
407 }
408
409
410 \f
411
412 void
413 psppire_axis_append (PsppireAxis *a, gint size)
414 {
415   psppire_axis_append_n (a, 1, size);
416 }
417
418
419 /* Append N_UNITS of size SIZE to A.
420    The value of the "padding" property will be added to SIZE
421    unit before appending.
422 */
423 void
424 psppire_axis_append_n (PsppireAxis *a, gint n_units, gint size)
425 {
426   struct axis_node *node;
427
428   if  (n_units == 0)
429     return;
430
431   node = pool_malloc (a->pool, sizeof *node);
432
433   tower_insert (&a->unit_tower, n_units, &node->unit_node, NULL);
434   tower_insert (&a->pixel_tower, (size + a->padding) * n_units,
435     &node->pixel_node, NULL);
436 }
437
438
439 /* Split the node of both towers at POSN */
440 static void
441 split (PsppireAxis *a, gint posn)
442 {
443   unsigned long int existing_unit_size;
444   unsigned long int existing_pixel_size;
445   unsigned long int start;
446   gdouble fraction;
447   struct axis_node *new_node ;
448   struct tower_node *n;
449   struct axis_node *existing_node;
450
451   g_return_if_fail (posn <= tower_height (&a->unit_tower));
452
453   /* Nothing needs to be done */
454   if ( posn == 0 || posn  == tower_height (&a->unit_tower))
455     return;
456
457   n = tower_lookup (&a->unit_tower, posn, &start);
458
459   existing_node = tower_data (n, struct axis_node, unit_node);
460
461   /* Nothing needs to be done, if the range element is already split here */
462   if ( posn - start == 0)
463     return;
464
465   existing_unit_size = tower_node_get_size (&existing_node->unit_node);
466   existing_pixel_size = tower_node_get_size (&existing_node->pixel_node);
467
468   fraction = (posn - start) / (gdouble) existing_unit_size;
469
470   new_node = pool_malloc (a->pool, sizeof (*new_node));
471
472   tower_resize (&a->unit_tower, &existing_node->unit_node, posn - start);
473
474   tower_resize (&a->pixel_tower, &existing_node->pixel_node,
475                 rintf (fraction * existing_pixel_size));
476
477   tower_insert (&a->unit_tower,
478                 existing_unit_size - (posn - start),
479                 &new_node->unit_node,
480                 tower_next (&a->unit_tower, &existing_node->unit_node));
481
482
483   tower_insert (&a->pixel_tower,
484                 rintf (existing_pixel_size * (1 - fraction)),
485                 &new_node->pixel_node,
486                 tower_next (&a->pixel_tower, &existing_node->pixel_node));
487 }
488
489
490 /* Insert a new unit of size SIZE before POSN.
491    The value of the "padding" property will be added to SIZE before
492    the unit is inserted.
493  */
494 void
495 psppire_axis_insert (PsppireAxis *a, gint posn, gint size)
496 {
497   struct axis_node *before = NULL;
498   struct axis_node *new_node;
499
500   g_return_if_fail ( posn >= 0);
501   g_return_if_fail ( posn <= tower_height (&a->unit_tower));
502
503   if ( posn < tower_height (&a->unit_tower))
504     {
505       unsigned long int start = 0;
506       struct tower_node *n;
507
508       split (a, posn);
509
510       n = tower_lookup (&a->unit_tower, posn, &start);
511       g_assert (posn == start);
512
513       before = tower_data (n, struct axis_node, unit_node);
514     }
515
516   new_node = pool_malloc (a->pool, sizeof (*new_node));
517
518   tower_insert (&a->unit_tower,
519                 1,
520                 &new_node->unit_node,
521                 before ? &before->unit_node : NULL);
522
523   tower_insert (&a->pixel_tower,
524                 size + a->padding,
525                 &new_node->pixel_node,
526                 before ? &before->pixel_node : NULL);
527 }
528
529
530 /* Make the element at POSN singular.
531    Return a pointer to the node for this element */
532 static struct axis_node *
533 make_single (PsppireAxis *a, gint posn)
534 {
535   unsigned long int start;
536   struct tower_node *n;
537
538   g_return_val_if_fail (posn < tower_height (&a->unit_tower), NULL);
539
540   n = tower_lookup (&a->unit_tower, posn, &start);
541
542   if ( 1 != tower_node_get_size (n))
543     {
544       split (a, posn + 1);
545       n = tower_lookup (&a->unit_tower, posn, &start);
546
547       if ( 1 != tower_node_get_size (n))
548         {
549           split (a, posn);
550           n = tower_lookup (&a->unit_tower, posn, &start);
551         }
552     }
553
554   g_assert (1 == tower_node_get_size (n));
555
556   return tower_data (n, struct axis_node, unit_node);
557 }
558
559
560 /*
561   Set the size of the unit at POSN to be SIZE plus the
562   current value of "padding"
563  */
564 void
565 psppire_axis_resize (PsppireAxis *axis, gint posn, glong size)
566 {
567   struct axis_node *an;
568   g_return_if_fail (posn >= 0);
569   g_return_if_fail (size > 0);
570
571   /* Silently ignore this request if the position is greater than the number of
572      units in the axis */
573   if (posn >= tower_height (&axis->unit_tower))
574     return ;
575
576   an = make_single (axis, posn);
577
578   tower_resize (&axis->pixel_tower, &an->pixel_node, size + axis->padding);
579
580   g_signal_emit (axis, signals[RESIZE_UNIT], 0, posn, size + axis->padding);
581 }
582
583
584
585
586
587
588 void
589 psppire_axis_clear (PsppireAxis *a)
590 {
591   pool_destroy (a->pool);
592   a->pool = pool_create ();
593
594   tower_init (&a->pixel_tower);
595   tower_init (&a->unit_tower);
596 }
597
598
599
600 void
601 psppire_axis_delete (PsppireAxis *a, gint first, gint n_units)
602 {
603   gint units_to_delete = n_units;
604   unsigned long int start;
605   struct tower_node *unit_node ;
606   g_return_if_fail (first + n_units <= tower_height (&a->unit_tower));
607
608   split (a, first);
609   split (a, first + n_units);
610
611   unit_node = tower_lookup (&a->unit_tower, first, &start);
612   g_assert (start == first);
613
614   while (units_to_delete > 0)
615     {
616       struct tower_node *next_unit_node;
617       struct axis_node *an = tower_data (unit_node,
618                                          struct axis_node, unit_node);
619
620       g_assert (unit_node == &an->unit_node);
621       g_assert (unit_node->size <= n_units);
622
623       units_to_delete -= unit_node->size;
624
625       next_unit_node = tower_next (&a->unit_tower, unit_node);
626
627       tower_delete (&a->unit_tower, unit_node);
628       tower_delete (&a->pixel_tower, &an->pixel_node);
629
630       pool_free (a->pool, an);
631
632       unit_node = next_unit_node;
633     }
634 }