table: Get rid of table_class.
[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 #include "output/tab.h"
34
35 #include "gl/xalloc.h"
36
37 /* Increases TABLE's reference count, indicating that it has an additional
38    owner.  An table that is shared among multiple owners must not be
39    modified. */
40 struct table *
41 table_ref (const struct table *table_)
42 {
43   struct table *table = CONST_CAST (struct table *, table_);
44   table->ref_cnt++;
45   return table;
46 }
47
48 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
49    If TABLE no longer has any owners, it is freed. */
50 void
51 table_unref (struct table *table)
52 {
53   if (table != NULL)
54     {
55       assert (table->ref_cnt > 0);
56       if (--table->ref_cnt == 0)
57         tab_destroy (table);
58     }
59 }
60
61 /* Returns true if TABLE has more than one owner.  A table item that is shared
62    among multiple owners must not be modified. */
63 bool
64 table_is_shared (const struct table *table)
65 {
66   return table->ref_cnt > 1;
67 }
68
69 /* Sets the number of left header columns in TABLE to HL. */
70 void
71 table_set_hl (struct table *table, int hl)
72 {
73   assert (!table_is_shared (table));
74   table->h[TABLE_HORZ][0] = hl;
75 }
76
77 /* Sets the number of right header columns in TABLE to HR. */
78 void
79 table_set_hr (struct table *table, int hr)
80 {
81   assert (!table_is_shared (table));
82   table->h[TABLE_HORZ][1] = hr;
83 }
84
85 /* Sets the number of top header rows in TABLE to HT. */
86 void
87 table_set_ht (struct table *table, int ht)
88 {
89   assert (!table_is_shared (table));
90   table->h[TABLE_VERT][0] = ht;
91 }
92
93 /* Sets the number of top header rows in TABLE to HB. */
94 void
95 table_set_hb (struct table *table, int hb)
96 {
97   assert (!table_is_shared (table));
98   table->h[TABLE_VERT][1] = hb;
99 }
100 \f
101 struct area_style *
102 area_style_clone (struct pool *pool, const struct area_style *old)
103 {
104   struct area_style *new = pool_malloc (pool, sizeof *new);
105   *new = *old;
106   if (new->font_style.typeface)
107     new->font_style.typeface = pool_strdup (pool, new->font_style.typeface);
108   return new;
109 }
110
111 void
112 area_style_free (struct area_style *style)
113 {
114   if (style)
115     {
116       free (style->font_style.typeface);
117       free (style);
118     }
119 }
120
121 /* Initializes CELL with the contents of the table cell at column X and row Y
122    within TABLE.  When CELL is no longer needed, the caller is responsible for
123    freeing it by calling table_cell_free(CELL).
124
125    The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
126 void
127 table_get_cell (const struct table *table, int x, int y,
128                 struct table_cell *cell)
129 {
130   assert (x >= 0 && x < table->n[TABLE_HORZ]);
131   assert (y >= 0 && y < table->n[TABLE_VERT]);
132
133   static const struct area_style default_style = AREA_STYLE_INITIALIZER;
134   cell->style = &default_style;
135
136   tab_get_cell (table, x, y, cell);
137 }
138
139 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
140    representing a rule running alongside one of the cells in TABLE.
141
142    Suppose NC is the number of columns in TABLE and NR is the number of rows.
143    Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
144    (0,0), the return value is the rule that runs vertically on the left side of
145    cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
146    cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
147    cell (NC-1,0).
148
149    The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
150    within a 7x7 table.  The '|' characters at the intersection of the X labels
151    and Y labels show the rule whose style would be returned by calling
152    table_get_rule with those X and Y values:
153
154                            0  1  2  3  4  5  6  7
155                            +--+--+--+--+--+--+--+
156                          0 |  |  |  |  |  |  |  |
157                            +--+--+--+--+--+--+--+
158                          1 |  |  |  |  |  |  |  |
159                            +--+--+--+--+--+--+--+
160                          2 |  |  |  |  |  |  |  |
161                            +--+--+--+--+--+--+--+
162                          3 |  |  |  |  |  |  |  |
163                            +--+--+--+--+--+--+--+
164                          4 |  |  |  |  |  |  |  |
165                            +--+--+--+--+--+--+--+
166                          5 |  |  |  |  |  |  |  |
167                            +--+--+--+--+--+--+--+
168                          6 |  |  |  |  |  |  |  |
169                            +--+--+--+--+--+--+--+
170
171    Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
172    (X,Y) = (0,0), the return value is the rule that runs horizontally above
173    the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
174    between that cell and cell (0,1); and so on, up to (0,NR), which runs
175    horizontally below cell (0,NR-1). */
176 int
177 table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
178                 struct cell_color *color)
179 {
180   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
181   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
182   *color = (struct cell_color) CELL_COLOR_BLACK;
183   return tab_get_rule (table, axis, x, y, color);
184 }
185
186 void
187 table_cell_format_footnote_markers (const struct table_cell *cell,
188                                     struct string *s)
189 {
190   for (size_t i = 0; i < cell->n_footnotes; i++)
191     {
192       if (i)
193         ds_put_byte (s, ',');
194       ds_put_cstr (s, cell->footnotes[i]->marker);
195     }
196 }
197
198 static const struct footnote **
199 add_footnotes (const struct footnote **refs, size_t n_refs,
200                const struct footnote **footnotes, size_t *allocated, size_t *n)
201 {
202   for (size_t i = 0; i < n_refs; i++)
203     {
204       const struct footnote *f = refs[i];
205       if (f->idx >= *allocated)
206         {
207           size_t new_allocated = (f->idx + 1) * 2;
208           footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
209           while (*allocated < new_allocated)
210             footnotes[(*allocated)++] = NULL;
211         }
212       footnotes[f->idx] = f;
213       if (f->idx >= *n)
214         *n = f->idx + 1;
215     }
216   return footnotes;
217 }
218
219 size_t
220 table_collect_footnotes (const struct table_item *item,
221                          const struct footnote ***footnotesp)
222 {
223   const struct footnote **footnotes = NULL;
224   size_t allocated = 0;
225   size_t n = 0;
226
227   struct table *t = item->table;
228   for (int y = 0; y < table_nr (t); y++)
229     {
230       struct table_cell cell;
231       for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
232         {
233           table_get_cell (t, x, y, &cell);
234
235           if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
236             footnotes = add_footnotes (cell.footnotes, cell.n_footnotes,
237                                        footnotes, &allocated, &n);
238         }
239     }
240
241   const struct table_item_text *title = table_item_get_title (item);
242   if (title)
243     footnotes = add_footnotes (title->footnotes, title->n_footnotes,
244                                footnotes, &allocated, &n);
245
246   const struct table_item_layers *layers = table_item_get_layers (item);
247   if (layers)
248     {
249       for (size_t i = 0; i < layers->n_layers; i++)
250         footnotes = add_footnotes (layers->layers[i].footnotes,
251                                    layers->layers[i].n_footnotes,
252                                    footnotes, &allocated, &n);
253     }
254
255   const struct table_item_text *caption = table_item_get_caption (item);
256   if (caption)
257     footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
258                                footnotes, &allocated, &n);
259
260   size_t n_nonnull = 0;
261   for (size_t i = 0; i < n; i++)
262     if (footnotes[i])
263       footnotes[n_nonnull++] = footnotes[i];
264
265   *footnotesp = footnotes;
266   return n_nonnull;
267 }
268 \f
269 /* Returns a table that contains a single cell, whose contents are the
270    left-aligned TEXT.  */
271 struct table *
272 table_from_string (const char *text)
273 {
274   struct tab_table *t = tab_create (1, 1);
275   tab_text (t, 0, 0, TAB_LEFT, text);
276   return &t->table;
277 }
278 \f
279 const char *
280 table_halign_to_string (enum table_halign halign)
281 {
282   switch (halign)
283     {
284     case TABLE_HALIGN_LEFT: return "left";
285     case TABLE_HALIGN_CENTER: return "center";
286     case TABLE_HALIGN_RIGHT: return "right";
287     case TABLE_HALIGN_DECIMAL: return "decimal";
288     case TABLE_HALIGN_MIXED: return "mixed";
289     default: return "**error**";
290     }
291 }
292
293 const char *
294 table_valign_to_string (enum table_valign valign)
295 {
296   switch (valign)
297     {
298     case TABLE_VALIGN_TOP: return "top";
299     case TABLE_VALIGN_CENTER: return "center";
300     case TABLE_VALIGN_BOTTOM: return "bottom";
301     default: return "**error**";
302     }
303 }
304
305 enum table_halign
306 table_halign_interpret (enum table_halign halign, bool numeric)
307 {
308   switch (halign)
309     {
310     case TABLE_HALIGN_LEFT:
311     case TABLE_HALIGN_CENTER:
312     case TABLE_HALIGN_RIGHT:
313       return halign;
314
315     case TABLE_HALIGN_MIXED:
316       return numeric ? TABLE_HALIGN_RIGHT : TABLE_HALIGN_LEFT;
317
318     case TABLE_HALIGN_DECIMAL:
319       return TABLE_HALIGN_DECIMAL;
320
321     default:
322       NOT_REACHED ();
323     }
324 }
325
326 void
327 font_style_copy (struct font_style *dst, const struct font_style *src)
328 {
329   *dst = *src;
330   if (dst->typeface)
331     dst->typeface = xstrdup (dst->typeface);
332 }
333
334 void
335 font_style_uninit (struct font_style *font)
336 {
337   if (font)
338     free (font->typeface);
339 }
340
341 void
342 area_style_copy (struct area_style *dst, const struct area_style *src)
343 {
344   font_style_copy (&dst->font_style, &src->font_style);
345   dst->cell_style = src->cell_style;
346 }
347
348 void
349 area_style_uninit (struct area_style *area)
350 {
351   if (area)
352     font_style_uninit (&area->font_style);
353 }
354
355 const char *
356 table_stroke_to_string (enum table_stroke stroke)
357 {
358   switch (stroke)
359     {
360     case TABLE_STROKE_NONE: return "none";
361     case TABLE_STROKE_SOLID: return "solid";
362     case TABLE_STROKE_DASHED: return "dashed";
363     case TABLE_STROKE_THICK: return "thick";
364     case TABLE_STROKE_THIN: return "thin";
365     case TABLE_STROKE_DOUBLE: return "double";
366     default:
367       return "**error**";
368     }
369 }
370
371 void
372 cell_color_dump (const struct cell_color *c)
373 {
374   if (c->alpha != 255)
375     printf ("rgba(%d, %d, %d, %d)", c->r, c->g, c->b, c->alpha);
376   else
377     printf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8, c->r, c->g, c->b);
378 }
379
380 void
381 font_style_dump (const struct font_style *f)
382 {
383   printf ("%s %dpx ", f->typeface, f->size);
384   cell_color_dump (&f->fg[0]);
385   putchar ('/');
386   cell_color_dump (&f->bg[0]);
387   if (!cell_color_equal (&f->fg[0], &f->fg[1])
388       || !cell_color_equal (&f->bg[0], &f->bg[1]))
389     {
390       printf (" alt=");
391       cell_color_dump (&f->fg[1]);
392       putchar ('/');
393       cell_color_dump (&f->bg[1]);
394     }
395   if (f->bold)
396     fputs (" bold", stdout);
397   if (f->italic)
398     fputs (" italic", stdout);
399   if (f->underline)
400     fputs (" underline", stdout);
401 }
402
403 void
404 cell_style_dump (const struct cell_style *c)
405 {
406   fputs (table_halign_to_string (c->halign), stdout);
407   if (c->halign == TABLE_HALIGN_DECIMAL)
408     printf ("(%.2gpx)", c->decimal_offset);
409   printf (" %s", table_valign_to_string (c->valign));
410   printf (" %d,%d,%d,%dpx",
411           c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1],
412           c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]);
413 }