Make psppire-axis thread safer
[pspp-builds.git] / src / ui / gui / sheet / psppire-axis.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2009  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   gint unit;
104   glong size;
105
106   g_return_val_if_fail (pixel >= 0, -1);
107
108   g_mutex_lock (a->mutex);
109
110   size = tower_height (&a->pixel_tower);
111
112   if (pixel >= size)
113     {
114       gint n_items = tower_height (&a->unit_tower);
115       glong extra = pixel - size;
116
117       unit = n_items - 1 + DIV_RND_UP (extra,  a->default_size);
118       goto finish;
119     }
120
121
122   n = tower_lookup (&a->pixel_tower, pixel, &start);
123   an = tower_data (n, struct axis_node, pixel_node);
124
125   fraction = (pixel - start) / (gdouble) tower_node_get_size (&an->pixel_node);
126
127   unit = tower_node_get_level (&an->unit_node)
128     + fraction * tower_node_get_size (&an->unit_node);
129
130  finish:
131
132   g_mutex_unlock (a->mutex);
133
134   return unit;
135 }
136
137
138 gint
139 psppire_axis_unit_count (const PsppireAxis *a)
140 {
141   glong filler = 0;
142   glong actual_size;
143   gint n_units;
144
145   g_mutex_lock (a->mutex);
146
147   actual_size = tower_height (&a->pixel_tower);
148
149   if ( actual_size < a->min_extent )
150     filler = DIV_RND_UP (a->min_extent - actual_size, a->default_size);
151
152   n_units = tower_height (&a->unit_tower) + filler;
153
154   g_mutex_unlock (a->mutex);
155
156   return n_units;
157 }
158
159
160 /* Return the starting pixel of UNIT */
161 glong
162 psppire_axis_start_pixel (const PsppireAxis *a, gint unit)
163 {
164   gdouble fraction;
165   struct tower_node *n ;
166   struct axis_node *an;
167   glong pixel ;
168
169   unsigned long int start;
170
171   gint the_count, size ;
172
173   g_mutex_lock (a->mutex);
174
175   the_count =  tower_height (&a->unit_tower);
176   size = tower_height (&a->pixel_tower);
177
178   if ( unit >= the_count)
179     {
180       pixel = size + (unit - the_count) * a->default_size;
181       goto finish;
182     }
183
184   if ( unit < 0)
185     goto error;
186
187   if ( unit >= tower_height (&a->unit_tower))
188     goto error;
189
190   n = tower_lookup (&a->unit_tower, unit, &start);
191
192   an = tower_data (n, struct axis_node, unit_node);
193
194   fraction = (unit - start) / (gdouble) tower_node_get_size (&an->unit_node);
195
196   pixel =  tower_node_get_level (&an->pixel_node) +
197     nearbyint (fraction * tower_node_get_size (&an->pixel_node));
198
199  finish:
200   g_mutex_unlock (a->mutex);
201   return pixel;
202
203  error:
204   g_mutex_unlock (a->mutex);
205   return  -1;
206 }
207
208 gint
209 psppire_axis_unit_size (const PsppireAxis *axis, gint unit)
210 {
211   struct tower_node *n ;
212   struct axis_node *an;
213   gint size;
214
215   unsigned long int start;
216
217   g_mutex_lock (axis->mutex);
218
219   if  (unit >= tower_height (&axis->unit_tower))
220     {
221       size = axis->default_size;
222       goto finish;
223     }
224
225   if ( unit < 0)
226     {
227       size = 0;
228       goto finish;
229     }
230
231   if ( unit >= tower_height (&axis->unit_tower))
232     {
233       size = 0;
234       goto finish;
235     }
236
237   n = tower_lookup (&axis->unit_tower, unit, &start);
238
239   an = tower_data (n, struct axis_node, unit_node);
240
241   size = nearbyint (tower_node_get_size (&an->pixel_node)
242                     / (gdouble) tower_node_get_size (&an->unit_node));
243  finish:
244   g_mutex_unlock (axis->mutex);
245   return size;
246 }
247
248
249
250 /* --- functions --- */
251 /**
252  * psppire_axis_get_type:
253  * @returns: the type ID for accelerator groups.
254  */
255 GType
256 psppire_axis_get_type (void)
257 {
258   static GType object_type = 0;
259
260   if (!object_type)
261     {
262       static const GTypeInfo object_info = {
263         sizeof (PsppireAxisClass),
264         (GBaseInitFunc) NULL,
265         (GBaseFinalizeFunc) NULL,
266         (GClassInitFunc) psppire_axis_class_init,
267         NULL,   /* class_finalize */
268         NULL,   /* class_data */
269         sizeof (PsppireAxis),
270         0,      /* n_preallocs */
271         (GInstanceInitFunc) psppire_axis_init,
272       };
273
274       object_type = g_type_register_static (G_TYPE_OBJECT,
275                                             "PsppireAxis",
276                                             &object_info, 0);
277
278     }
279
280   return object_type;
281 }
282
283 enum
284   {
285     PROP_0,
286     PROP_MIN_EXTENT,
287     PROP_DEFAULT_SIZE,
288     PROP_PADDING
289   };
290
291
292 static void
293 psppire_axis_get_property (GObject         *object,
294                            guint            prop_id,
295                            GValue          *value,
296                            GParamSpec      *pspec)
297 {
298   PsppireAxis *axis = PSPPIRE_AXIS (object);
299
300   switch (prop_id)
301     {
302     case PROP_PADDING:
303       g_value_set_int (value, axis->padding);
304       break;
305     case PROP_MIN_EXTENT:
306       g_value_set_long (value, axis->min_extent);
307       break;
308     case PROP_DEFAULT_SIZE:
309       g_value_set_int (value, axis->default_size);
310       break;
311     default:
312       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
313       break;
314     };
315 }
316
317
318 static void
319 psppire_axis_set_property (GObject         *object,
320                            guint            prop_id,
321                            const GValue    *value,
322                            GParamSpec      *pspec)
323 {
324   PsppireAxis *axis = PSPPIRE_AXIS (object);
325
326   switch (prop_id)
327     {
328     case PROP_PADDING:
329       {
330         const gint old_value = axis->padding;
331         axis->padding = g_value_get_int (value);
332         axis_increment (axis, axis->padding - old_value);
333       }
334       break;
335     case PROP_MIN_EXTENT:
336       axis->min_extent = g_value_get_long (value);
337       break;
338     case PROP_DEFAULT_SIZE:
339       axis->default_size = g_value_get_int (value);
340       break;
341     default:
342       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
343       break;
344     };
345 }
346
347
348 static void
349 psppire_axis_class_init (PsppireAxisClass *class)
350 {
351   GObjectClass *object_class = G_OBJECT_CLASS (class);
352
353   GParamSpec *padding_spec;
354   GParamSpec *min_extent_spec;
355   GParamSpec *default_size_spec;
356
357   parent_class = g_type_class_peek_parent (class);
358
359   object_class->set_property = psppire_axis_set_property;
360   object_class->get_property = psppire_axis_get_property;
361
362
363   min_extent_spec =
364     g_param_spec_long ("minimum-extent",
365                        "Minimum Extent",
366                        "The smallest extent to which the axis will provide units (typically set to the height/width of the associated widget).",
367                        0, G_MAXLONG,
368                        0,
369                        G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE );
370
371   g_object_class_install_property (object_class,
372                                    PROP_MIN_EXTENT,
373                                    min_extent_spec);
374
375
376   default_size_spec =
377     g_param_spec_int ("default-size",
378                       "Default Size",
379                       "The size given to units which haven't been explicity inserted",
380                       0, G_MAXINT,
381                       25,
382                       G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE );
383
384
385   g_object_class_install_property (object_class,
386                                    PROP_DEFAULT_SIZE,
387                                    default_size_spec);
388
389   padding_spec =
390     g_param_spec_int ("padding",
391                       "Padding",
392                       "Extra space implicitly added to each unit",
393                       0, G_MAXINT,
394                       0,
395                       G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE );
396
397
398   g_object_class_install_property (object_class,
399                                    PROP_PADDING,
400                                    padding_spec);
401
402
403
404   signals[RESIZE_UNIT] =
405     g_signal_new ("resize-unit",
406                   G_TYPE_FROM_CLASS (object_class),
407                   G_SIGNAL_RUN_LAST,
408                   0,
409                   NULL, NULL,
410                   psppire_marshal_VOID__INT_LONG,
411                   G_TYPE_NONE,
412                   2,
413                   G_TYPE_INT,
414                   G_TYPE_LONG
415                   );
416
417
418   object_class->finalize = psppire_axis_finalize;
419 }
420
421
422 static void
423 psppire_axis_init (PsppireAxis *axis)
424 {
425   axis->mutex = g_mutex_new ();
426   tower_init (&axis->pixel_tower);
427   tower_init (&axis->unit_tower);
428
429   axis->pool = pool_create ();
430   axis->padding = 0;
431 }
432
433
434 static void
435 psppire_axis_finalize (GObject *object)
436 {
437   PsppireAxis *a = PSPPIRE_AXIS (object);
438   pool_destroy (a->pool);
439   g_mutex_free (a->mutex);
440
441   G_OBJECT_CLASS (parent_class)->finalize (object);
442 }
443
444 /**
445  * psppire_axis_new:
446  * @returns: a new #PsppireAxis object
447  *
448  * Creates a new #PsppireAxis.
449  */
450 PsppireAxis*
451 psppire_axis_new (void)
452 {
453   return g_object_new (G_TYPE_PSPPIRE_AXIS, NULL);
454 }
455
456
457 \f
458
459 void
460 psppire_axis_append (PsppireAxis *a, gint size)
461 {
462   psppire_axis_append_n (a, 1, size);
463 }
464
465
466 /* Append N_UNITS of size SIZE to A.
467    The value of the "padding" property will be added to SIZE
468    unit before appending.
469 */
470 void
471 psppire_axis_append_n (PsppireAxis *a, gint n_units, gint size)
472 {
473   struct axis_node *node;
474
475   g_mutex_lock (a->mutex);
476
477   if  (n_units == 0)
478     goto finish;
479
480   node = pool_malloc (a->pool, sizeof *node);
481
482   tower_insert (&a->unit_tower, n_units, &node->unit_node, NULL);
483   tower_insert (&a->pixel_tower, (size + a->padding) * n_units,
484     &node->pixel_node, NULL);
485
486  finish:
487   g_mutex_unlock (a->mutex);
488   return;
489 }
490
491
492 /* Split the node of both towers at POSN */
493 static void
494 split (PsppireAxis *a, gint posn)
495 {
496   unsigned long int existing_unit_size;
497   unsigned long int existing_pixel_size;
498   unsigned long int start;
499   gdouble fraction;
500   struct axis_node *new_node ;
501   struct tower_node *n;
502   struct axis_node *existing_node;
503
504   g_return_if_fail (posn <= tower_height (&a->unit_tower));
505
506   /* Nothing needs to be done */
507   if ( posn == 0 || posn  == tower_height (&a->unit_tower))
508     return;
509
510   n = tower_lookup (&a->unit_tower, posn, &start);
511
512   existing_node = tower_data (n, struct axis_node, unit_node);
513
514   /* Nothing needs to be done, if the range element is already split here */
515   if ( posn - start == 0)
516     return;
517
518   existing_unit_size = tower_node_get_size (&existing_node->unit_node);
519   existing_pixel_size = tower_node_get_size (&existing_node->pixel_node);
520
521   fraction = (posn - start) / (gdouble) existing_unit_size;
522
523   new_node = pool_malloc (a->pool, sizeof (*new_node));
524
525   tower_resize (&a->unit_tower, &existing_node->unit_node, posn - start);
526
527   tower_resize (&a->pixel_tower, &existing_node->pixel_node,
528                 nearbyintf (fraction * existing_pixel_size));
529
530   tower_insert (&a->unit_tower,
531                 existing_unit_size - (posn - start),
532                 &new_node->unit_node,
533                 tower_next (&a->unit_tower, &existing_node->unit_node));
534
535
536   tower_insert (&a->pixel_tower,
537                 nearbyintf (existing_pixel_size * (1 - fraction)),
538                 &new_node->pixel_node,
539                 tower_next (&a->pixel_tower, &existing_node->pixel_node));
540 }
541
542
543 /* Insert a new unit of size SIZE before POSN.
544    The value of the "padding" property will be added to SIZE before
545    the unit is inserted.
546  */
547 void
548 psppire_axis_insert (PsppireAxis *a, gint posn, gint size)
549 {
550   struct axis_node *before = NULL;
551   struct axis_node *new_node;
552
553   g_return_if_fail ( posn >= 0);
554   g_return_if_fail ( posn <= tower_height (&a->unit_tower));
555
556   g_mutex_lock (a->mutex);
557
558   if ( posn < tower_height (&a->unit_tower))
559     {
560       unsigned long int start = 0;
561       struct tower_node *n;
562
563       split (a, posn);
564
565       n = tower_lookup (&a->unit_tower, posn, &start);
566       g_assert (posn == start);
567
568       before = tower_data (n, struct axis_node, unit_node);
569     }
570
571   new_node = pool_malloc (a->pool, sizeof (*new_node));
572
573   tower_insert (&a->unit_tower,
574                 1,
575                 &new_node->unit_node,
576                 before ? &before->unit_node : NULL);
577
578   tower_insert (&a->pixel_tower,
579                 size + a->padding,
580                 &new_node->pixel_node,
581                 before ? &before->pixel_node : NULL);
582
583   g_mutex_unlock (a->mutex);
584 }
585
586
587 /* Make the element at POSN singular.
588    Return a pointer to the node for this element */
589 static struct axis_node *
590 make_single (PsppireAxis *a, gint posn)
591 {
592   unsigned long int start;
593   struct tower_node *n;
594
595   g_return_val_if_fail (posn < tower_height (&a->unit_tower), NULL);
596
597   n = tower_lookup (&a->unit_tower, posn, &start);
598
599   if ( 1 != tower_node_get_size (n))
600     {
601       split (a, posn + 1);
602       n = tower_lookup (&a->unit_tower, posn, &start);
603
604       if ( 1 != tower_node_get_size (n))
605         {
606           split (a, posn);
607           n = tower_lookup (&a->unit_tower, posn, &start);
608         }
609     }
610
611   g_assert (1 == tower_node_get_size (n));
612
613   return tower_data (n, struct axis_node, unit_node);
614 }
615
616
617 /*
618   Set the size of the unit at POSN to be SIZE plus the
619   current value of "padding"
620  */
621 void
622 psppire_axis_resize (PsppireAxis *axis, gint posn, glong size)
623 {
624   struct axis_node *an;
625   g_return_if_fail (posn >= 0);
626   g_return_if_fail (size > 0);
627
628   g_mutex_lock (axis->mutex);
629
630   /* Silently ignore this request if the position is greater than the number of
631      units in the axis */
632   if (posn >= tower_height (&axis->unit_tower))
633     goto error;
634
635   an = make_single (axis, posn);
636
637   tower_resize (&axis->pixel_tower, &an->pixel_node, size + axis->padding);
638
639  finish:
640   g_mutex_unlock (axis->mutex);
641   g_signal_emit (axis, signals[RESIZE_UNIT], 0, posn, size + axis->padding);
642   return;
643
644  error:
645   g_mutex_unlock (axis->mutex);
646   return;
647 }
648
649
650
651
652
653
654 void
655 psppire_axis_clear (PsppireAxis *a)
656 {
657   g_mutex_lock (a->mutex);
658   pool_destroy (a->pool);
659   a->pool = pool_create ();
660
661   tower_init (&a->pixel_tower);
662   tower_init (&a->unit_tower);
663   g_mutex_unlock (a->mutex);
664 }
665
666
667
668 void
669 psppire_axis_delete (PsppireAxis *a, gint first, gint n_units)
670 {
671   gint units_to_delete = n_units;
672   unsigned long int start;
673   struct tower_node *unit_node ;
674   g_return_if_fail (first + n_units <= tower_height (&a->unit_tower));
675
676   g_mutex_lock (a->mutex);
677
678   split (a, first);
679   split (a, first + n_units);
680
681   unit_node = tower_lookup (&a->unit_tower, first, &start);
682   g_assert (start == first);
683
684   while (units_to_delete > 0)
685     {
686       struct tower_node *next_unit_node;
687       struct axis_node *an = tower_data (unit_node,
688                                          struct axis_node, unit_node);
689
690       g_assert (unit_node == &an->unit_node);
691       g_assert (unit_node->size <= n_units);
692
693       units_to_delete -= unit_node->size;
694
695       next_unit_node = tower_next (&a->unit_tower, unit_node);
696
697       tower_delete (&a->unit_tower, unit_node);
698       tower_delete (&a->pixel_tower, &an->pixel_node);
699
700       pool_free (a->pool, an);
701
702       unit_node = next_unit_node;
703     }
704   g_mutex_unlock (a->mutex);
705 }