output: Remove table_unshare.
[pspp] / src / output / table.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2011, 2014, 2016 Free Software Foundation, Inc.
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
19 #include "output/table.h"
20 #include "output/table-provider.h"
21
22 #include <assert.h>
23 #include <inttypes.h>
24 #include <stdlib.h>
25
26 #include "data/format.h"
27 #include "libpspp/assertion.h"
28 #include "libpspp/cast.h"
29 #include "libpspp/compiler.h"
30 #include "libpspp/pool.h"
31 #include "libpspp/str.h"
32 #include "output/table-item.h"
33
34 #include "gl/xalloc.h"
35
36 /* Increases TABLE's reference count, indicating that it has an additional
37    owner.  An table that is shared among multiple owners must not be
38    modified. */
39 struct table *
40 table_ref (const struct table *table_)
41 {
42   struct table *table = CONST_CAST (struct table *, table_);
43   table->ref_cnt++;
44   return table;
45 }
46
47 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
48    If TABLE no longer has any owners, it is freed. */
49 void
50 table_unref (struct table *table)
51 {
52   if (table != NULL)
53     {
54       assert (table->ref_cnt > 0);
55       if (--table->ref_cnt == 0)
56         table->klass->destroy (table);
57     }
58 }
59
60 /* Returns true if TABLE has more than one owner.  A table item that is shared
61    among multiple owners must not be modified. */
62 bool
63 table_is_shared (const struct table *table)
64 {
65   return table->ref_cnt > 1;
66 }
67
68 /* Sets the number of left header columns in TABLE to HL. */
69 void
70 table_set_hl (struct table *table, int hl)
71 {
72   assert (!table_is_shared (table));
73   table->h[TABLE_HORZ][0] = hl;
74 }
75
76 /* Sets the number of right header columns in TABLE to HR. */
77 void
78 table_set_hr (struct table *table, int hr)
79 {
80   assert (!table_is_shared (table));
81   table->h[TABLE_HORZ][1] = hr;
82 }
83
84 /* Sets the number of top header rows in TABLE to HT. */
85 void
86 table_set_ht (struct table *table, int ht)
87 {
88   assert (!table_is_shared (table));
89   table->h[TABLE_VERT][0] = ht;
90 }
91
92 /* Sets the number of top header rows in TABLE to HB. */
93 void
94 table_set_hb (struct table *table, int hb)
95 {
96   assert (!table_is_shared (table));
97   table->h[TABLE_VERT][1] = hb;
98 }
99 \f
100 /* Initializes TABLE as a table of the specified CLASS, initially with a
101    reference count of 1.
102
103    TABLE initially has 0 rows and columns and no headers.  The table
104    implementation should update the numbers of rows and columns.  The table
105    implementation (or its client) may update the header rows and columns.
106
107    A table is an abstract class, that is, a plain struct table is not useful on
108    its own.  Thus, this function is normally called from the initialization
109    function of some subclass of table. */
110 void
111 table_init (struct table *table, const struct table_class *class)
112 {
113   table->klass = class;
114   table->n[TABLE_HORZ] = table->n[TABLE_VERT] = 0;
115   table->h[TABLE_HORZ][0] = table->h[TABLE_HORZ][1] = 0;
116   table->h[TABLE_VERT][0] = table->h[TABLE_VERT][1] = 0;
117   table->ref_cnt = 1;
118 }
119
120 /* Sets the number of columns in TABLE to NC. */
121 void
122 table_set_nc (struct table *table, int nc)
123 {
124   assert (!table_is_shared (table));
125   table->n[TABLE_HORZ] = nc;
126 }
127
128 /* Sets the number of rows in TABLE to NR. */
129 void
130 table_set_nr (struct table *table, int nr)
131 {
132   assert (!table_is_shared (table));
133   table->n[TABLE_VERT] = nr;
134 }
135 \f
136 struct area_style *
137 area_style_clone (struct pool *pool, const struct area_style *old)
138 {
139   struct area_style *new = pool_malloc (pool, sizeof *new);
140   *new = *old;
141   if (new->font_style.typeface)
142     new->font_style.typeface = pool_strdup (pool, new->font_style.typeface);
143   return new;
144 }
145
146 void
147 area_style_free (struct area_style *style)
148 {
149   if (style)
150     {
151       free (style->font_style.typeface);
152       free (style);
153     }
154 }
155
156 /* Initializes CELL with the contents of the table cell at column X and row Y
157    within TABLE.  When CELL is no longer needed, the caller is responsible for
158    freeing it by calling table_cell_free(CELL).
159
160    The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
161 void
162 table_get_cell (const struct table *table, int x, int y,
163                 struct table_cell *cell)
164 {
165   assert (x >= 0 && x < table->n[TABLE_HORZ]);
166   assert (y >= 0 && y < table->n[TABLE_VERT]);
167
168   static const struct area_style default_style = AREA_STYLE_INITIALIZER;
169   cell->style = &default_style;
170
171   table->klass->get_cell (table, x, y, cell);
172 }
173
174 /* Frees CELL, which should have been initialized by calling
175    table_get_cell(). */
176 void
177 table_cell_free (struct table_cell *cell)
178 {
179   if (cell->destructor != NULL)
180     cell->destructor (cell->destructor_aux);
181 }
182
183 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
184    representing a rule running alongside one of the cells in TABLE.
185
186    Suppose NC is the number of columns in TABLE and NR is the number of rows.
187    Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
188    (0,0), the return value is the rule that runs vertically on the left side of
189    cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
190    cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
191    cell (NC-1,0).
192
193    The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
194    within a 7x7 table.  The '|' characters at the intersection of the X labels
195    and Y labels show the rule whose style would be returned by calling
196    table_get_rule with those X and Y values:
197
198                            0  1  2  3  4  5  6  7
199                            +--+--+--+--+--+--+--+
200                          0 |  |  |  |  |  |  |  |
201                            +--+--+--+--+--+--+--+
202                          1 |  |  |  |  |  |  |  |
203                            +--+--+--+--+--+--+--+
204                          2 |  |  |  |  |  |  |  |
205                            +--+--+--+--+--+--+--+
206                          3 |  |  |  |  |  |  |  |
207                            +--+--+--+--+--+--+--+
208                          4 |  |  |  |  |  |  |  |
209                            +--+--+--+--+--+--+--+
210                          5 |  |  |  |  |  |  |  |
211                            +--+--+--+--+--+--+--+
212                          6 |  |  |  |  |  |  |  |
213                            +--+--+--+--+--+--+--+
214
215    Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
216    (X,Y) = (0,0), the return value is the rule that runs horizontally above
217    the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
218    between that cell and cell (0,1); and so on, up to (0,NR), which runs
219    horizontally below cell (0,NR-1). */
220 int
221 table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
222                 struct cell_color *color)
223 {
224   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
225   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
226   *color = (struct cell_color) CELL_COLOR_BLACK;
227   return table->klass->get_rule (table, axis, x, y, color);
228 }
229
230 void
231 table_cell_format_footnote_markers (const struct table_cell *cell,
232                                     struct string *s)
233 {
234   for (size_t i = 0; i < cell->n_footnotes; i++)
235     {
236       if (i)
237         ds_put_byte (s, ',');
238       ds_put_cstr (s, cell->footnotes[i]->marker);
239     }
240 }
241
242 static const struct footnote **
243 add_footnotes (const struct footnote **refs, size_t n_refs,
244                const struct footnote **footnotes, size_t *allocated, size_t *n)
245 {
246   for (size_t i = 0; i < n_refs; i++)
247     {
248       const struct footnote *f = refs[i];
249       if (f->idx >= *allocated)
250         {
251           size_t new_allocated = (f->idx + 1) * 2;
252           footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
253           while (*allocated < new_allocated)
254             footnotes[(*allocated)++] = NULL;
255         }
256       footnotes[f->idx] = f;
257       if (f->idx >= *n)
258         *n = f->idx + 1;
259     }
260   return footnotes;
261 }
262
263 size_t
264 table_collect_footnotes (const struct table_item *item,
265                          const struct footnote ***footnotesp)
266 {
267   const struct footnote **footnotes = NULL;
268   size_t allocated = 0;
269   size_t n = 0;
270
271   struct table *t = item->table;
272   for (int y = 0; y < table_nr (t); y++)
273     {
274       struct table_cell cell;
275       for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
276         {
277           table_get_cell (t, x, y, &cell);
278
279           if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
280             footnotes = add_footnotes (cell.footnotes, cell.n_footnotes,
281                                        footnotes, &allocated, &n);
282           table_cell_free (&cell);
283         }
284     }
285
286   const struct table_item_text *title = table_item_get_title (item);
287   if (title)
288     footnotes = add_footnotes (title->footnotes, title->n_footnotes,
289                                footnotes, &allocated, &n);
290
291   const struct table_item_text *caption = table_item_get_caption (item);
292   if (caption)
293     footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
294                                footnotes, &allocated, &n);
295
296   *footnotesp = footnotes;
297   return n;
298 }
299 \f
300 struct table_string
301   {
302     struct table table;
303     char *string;
304     enum table_halign halign;
305   };
306
307 static const struct table_class table_string_class;
308
309 /* Returns a table that contains a single cell, whose contents are S with
310    options OPTIONS (a combination of TAB_* values).  */
311 struct table *
312 table_from_string (enum table_halign halign, const char *s)
313 {
314   struct table_string *ts = xmalloc (sizeof *ts);
315   table_init (&ts->table, &table_string_class);
316   ts->table.n[TABLE_HORZ] = ts->table.n[TABLE_VERT] = 1;
317   ts->string = xstrdup (s);
318   ts->halign = halign;
319   return &ts->table;
320 }
321
322 static struct table_string *
323 table_string_cast (const struct table *table)
324 {
325   assert (table->klass == &table_string_class);
326   return UP_CAST (table, struct table_string, table);
327 }
328
329 static void
330 table_string_destroy (struct table *ts_)
331 {
332   struct table_string *ts = table_string_cast (ts_);
333   free (ts->string);
334   free (ts);
335 }
336
337 static void
338 table_string_get_cell (const struct table *ts_, int x UNUSED, int y UNUSED,
339                        struct table_cell *cell)
340 {
341   static const struct area_style styles[] = {
342 #define S(H) [H] = { AREA_STYLE_INITIALIZER__, .cell_style.halign = H }
343     S(TABLE_HALIGN_LEFT),
344     S(TABLE_HALIGN_CENTER),
345     S(TABLE_HALIGN_RIGHT),
346     S(TABLE_HALIGN_MIXED),
347     S(TABLE_HALIGN_DECIMAL),
348   };
349   struct table_string *ts = table_string_cast (ts_);
350   cell->d[TABLE_HORZ][0] = 0;
351   cell->d[TABLE_HORZ][1] = 1;
352   cell->d[TABLE_VERT][0] = 0;
353   cell->d[TABLE_VERT][1] = 1;
354   cell->options = 0;
355   cell->style = &styles[table_halign_interpret (ts->halign, false)];
356   cell->text = ts->string;
357   cell->n_footnotes = 0;
358   cell->destructor = NULL;
359 }
360
361
362 static int
363 table_string_get_rule (const struct table *ts UNUSED,
364                        enum table_axis axis UNUSED, int x UNUSED, int y UNUSED,
365                        struct cell_color *color UNUSED)
366 {
367   return TAL_0;
368 }
369
370 static const struct table_class table_string_class =
371   {
372     table_string_destroy,
373     table_string_get_cell,
374     table_string_get_rule,
375   };
376 \f
377 const char *
378 table_halign_to_string (enum table_halign halign)
379 {
380   switch (halign)
381     {
382     case TABLE_HALIGN_LEFT: return "left";
383     case TABLE_HALIGN_CENTER: return "center";
384     case TABLE_HALIGN_RIGHT: return "right";
385     case TABLE_HALIGN_DECIMAL: return "decimal";
386     case TABLE_HALIGN_MIXED: return "mixed";
387     default: return "**error**";
388     }
389 }
390
391 const char *
392 table_valign_to_string (enum table_valign valign)
393 {
394   switch (valign)
395     {
396     case TABLE_VALIGN_TOP: return "top";
397     case TABLE_VALIGN_CENTER: return "center";
398     case TABLE_VALIGN_BOTTOM: return "bottom";
399     default: return "**error**";
400     }
401 }
402
403 enum table_halign
404 table_halign_interpret (enum table_halign halign, bool numeric)
405 {
406   switch (halign)
407     {
408     case TABLE_HALIGN_LEFT:
409     case TABLE_HALIGN_CENTER:
410     case TABLE_HALIGN_RIGHT:
411       return halign;
412
413     case TABLE_HALIGN_MIXED:
414       return numeric ? TABLE_HALIGN_RIGHT : TABLE_HALIGN_LEFT;
415
416     case TABLE_HALIGN_DECIMAL:
417       return TABLE_HALIGN_DECIMAL;
418
419     default:
420       NOT_REACHED ();
421     }
422 }
423
424 void
425 font_style_copy (struct font_style *dst, const struct font_style *src)
426 {
427   *dst = *src;
428   if (dst->typeface)
429     dst->typeface = xstrdup (dst->typeface);
430 }
431
432 void
433 font_style_uninit (struct font_style *font)
434 {
435   if (font)
436     free (font->typeface);
437 }
438
439 void
440 area_style_copy (struct area_style *dst, const struct area_style *src)
441 {
442   font_style_copy (&dst->font_style, &src->font_style);
443   dst->cell_style = src->cell_style;
444 }
445
446 void
447 area_style_uninit (struct area_style *area)
448 {
449   if (area)
450     font_style_uninit (&area->font_style);
451 }
452
453 const char *
454 table_stroke_to_string (enum table_stroke stroke)
455 {
456   switch (stroke)
457     {
458     case TABLE_STROKE_NONE: return "none";
459     case TABLE_STROKE_SOLID: return "solid";
460     case TABLE_STROKE_DASHED: return "dashed";
461     case TABLE_STROKE_THICK: return "thick";
462     case TABLE_STROKE_THIN: return "thin";
463     case TABLE_STROKE_DOUBLE: return "double";
464     default:
465       return "**error**";
466     }
467 }
468
469 void
470 cell_color_dump (const struct cell_color *c)
471 {
472   if (c->alpha != 255)
473     printf ("rgba(%d, %d, %d, %d)", c->r, c->g, c->b, c->alpha);
474   else
475     printf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8, c->r, c->g, c->b);
476 }
477
478 void
479 font_style_dump (const struct font_style *f)
480 {
481   printf ("%s %dpx ", f->typeface, f->size);
482   cell_color_dump (&f->fg[0]);
483   putchar ('/');
484   cell_color_dump (&f->bg[0]);
485   if (!cell_color_equal (&f->fg[0], &f->fg[1])
486       || !cell_color_equal (&f->bg[0], &f->bg[1]))
487     {
488       printf (" alt=");
489       cell_color_dump (&f->fg[1]);
490       putchar ('/');
491       cell_color_dump (&f->bg[1]);
492     }
493   if (f->bold)
494     fputs (" bold", stdout);
495   if (f->italic)
496     fputs (" italic", stdout);
497   if (f->underline)
498     fputs (" underline", stdout);
499 }
500
501 void
502 cell_style_dump (const struct cell_style *c)
503 {
504   fputs (table_halign_to_string (c->halign), stdout);
505   if (c->halign == TABLE_HALIGN_DECIMAL)
506     printf ("(%.2gpx)", c->decimal_offset);
507   printf (" %s", table_valign_to_string (c->valign));
508   printf (" %d,%d,%d,%dpx",
509           c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1],
510           c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]);
511 }