output: Reimplement table_from_string in terms of tab.
[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         table->klass->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 /* Initializes TABLE as a table of the specified CLASS, initially with a
102    reference count of 1.
103
104    TABLE initially has 0 rows and columns and no headers.  The table
105    implementation should update the numbers of rows and columns.  The table
106    implementation (or its client) may update the header rows and columns.
107
108    A table is an abstract class, that is, a plain struct table is not useful on
109    its own.  Thus, this function is normally called from the initialization
110    function of some subclass of table. */
111 void
112 table_init (struct table *table, const struct table_class *class)
113 {
114   table->klass = class;
115   table->n[TABLE_HORZ] = table->n[TABLE_VERT] = 0;
116   table->h[TABLE_HORZ][0] = table->h[TABLE_HORZ][1] = 0;
117   table->h[TABLE_VERT][0] = table->h[TABLE_VERT][1] = 0;
118   table->ref_cnt = 1;
119 }
120
121 /* Sets the number of columns in TABLE to NC. */
122 void
123 table_set_nc (struct table *table, int nc)
124 {
125   assert (!table_is_shared (table));
126   table->n[TABLE_HORZ] = nc;
127 }
128
129 /* Sets the number of rows in TABLE to NR. */
130 void
131 table_set_nr (struct table *table, int nr)
132 {
133   assert (!table_is_shared (table));
134   table->n[TABLE_VERT] = nr;
135 }
136 \f
137 struct area_style *
138 area_style_clone (struct pool *pool, const struct area_style *old)
139 {
140   struct area_style *new = pool_malloc (pool, sizeof *new);
141   *new = *old;
142   if (new->font_style.typeface)
143     new->font_style.typeface = pool_strdup (pool, new->font_style.typeface);
144   return new;
145 }
146
147 void
148 area_style_free (struct area_style *style)
149 {
150   if (style)
151     {
152       free (style->font_style.typeface);
153       free (style);
154     }
155 }
156
157 /* Initializes CELL with the contents of the table cell at column X and row Y
158    within TABLE.  When CELL is no longer needed, the caller is responsible for
159    freeing it by calling table_cell_free(CELL).
160
161    The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
162 void
163 table_get_cell (const struct table *table, int x, int y,
164                 struct table_cell *cell)
165 {
166   assert (x >= 0 && x < table->n[TABLE_HORZ]);
167   assert (y >= 0 && y < table->n[TABLE_VERT]);
168
169   static const struct area_style default_style = AREA_STYLE_INITIALIZER;
170   cell->style = &default_style;
171
172   table->klass->get_cell (table, x, y, cell);
173 }
174
175 /* Frees CELL, which should have been initialized by calling
176    table_get_cell(). */
177 void
178 table_cell_free (struct table_cell *cell)
179 {
180   if (cell->destructor != NULL)
181     cell->destructor (cell->destructor_aux);
182 }
183
184 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
185    representing a rule running alongside one of the cells in TABLE.
186
187    Suppose NC is the number of columns in TABLE and NR is the number of rows.
188    Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
189    (0,0), the return value is the rule that runs vertically on the left side of
190    cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
191    cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
192    cell (NC-1,0).
193
194    The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
195    within a 7x7 table.  The '|' characters at the intersection of the X labels
196    and Y labels show the rule whose style would be returned by calling
197    table_get_rule with those X and Y values:
198
199                            0  1  2  3  4  5  6  7
200                            +--+--+--+--+--+--+--+
201                          0 |  |  |  |  |  |  |  |
202                            +--+--+--+--+--+--+--+
203                          1 |  |  |  |  |  |  |  |
204                            +--+--+--+--+--+--+--+
205                          2 |  |  |  |  |  |  |  |
206                            +--+--+--+--+--+--+--+
207                          3 |  |  |  |  |  |  |  |
208                            +--+--+--+--+--+--+--+
209                          4 |  |  |  |  |  |  |  |
210                            +--+--+--+--+--+--+--+
211                          5 |  |  |  |  |  |  |  |
212                            +--+--+--+--+--+--+--+
213                          6 |  |  |  |  |  |  |  |
214                            +--+--+--+--+--+--+--+
215
216    Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
217    (X,Y) = (0,0), the return value is the rule that runs horizontally above
218    the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
219    between that cell and cell (0,1); and so on, up to (0,NR), which runs
220    horizontally below cell (0,NR-1). */
221 int
222 table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
223                 struct cell_color *color)
224 {
225   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
226   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
227   *color = (struct cell_color) CELL_COLOR_BLACK;
228   return table->klass->get_rule (table, axis, x, y, color);
229 }
230
231 void
232 table_cell_format_footnote_markers (const struct table_cell *cell,
233                                     struct string *s)
234 {
235   for (size_t i = 0; i < cell->n_footnotes; i++)
236     {
237       if (i)
238         ds_put_byte (s, ',');
239       ds_put_cstr (s, cell->footnotes[i]->marker);
240     }
241 }
242
243 static const struct footnote **
244 add_footnotes (const struct footnote **refs, size_t n_refs,
245                const struct footnote **footnotes, size_t *allocated, size_t *n)
246 {
247   for (size_t i = 0; i < n_refs; i++)
248     {
249       const struct footnote *f = refs[i];
250       if (f->idx >= *allocated)
251         {
252           size_t new_allocated = (f->idx + 1) * 2;
253           footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
254           while (*allocated < new_allocated)
255             footnotes[(*allocated)++] = NULL;
256         }
257       footnotes[f->idx] = f;
258       if (f->idx >= *n)
259         *n = f->idx + 1;
260     }
261   return footnotes;
262 }
263
264 size_t
265 table_collect_footnotes (const struct table_item *item,
266                          const struct footnote ***footnotesp)
267 {
268   const struct footnote **footnotes = NULL;
269   size_t allocated = 0;
270   size_t n = 0;
271
272   struct table *t = item->table;
273   for (int y = 0; y < table_nr (t); y++)
274     {
275       struct table_cell cell;
276       for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
277         {
278           table_get_cell (t, x, y, &cell);
279
280           if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
281             footnotes = add_footnotes (cell.footnotes, cell.n_footnotes,
282                                        footnotes, &allocated, &n);
283           table_cell_free (&cell);
284         }
285     }
286
287   const struct table_item_text *title = table_item_get_title (item);
288   if (title)
289     footnotes = add_footnotes (title->footnotes, title->n_footnotes,
290                                footnotes, &allocated, &n);
291
292   const struct table_item_text *caption = table_item_get_caption (item);
293   if (caption)
294     footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
295                                footnotes, &allocated, &n);
296
297   *footnotesp = footnotes;
298   return n;
299 }
300 \f
301 /* Returns a table that contains a single cell, whose contents are the
302    left-aligned TEXT.  */
303 struct table *
304 table_from_string (const char *text)
305 {
306   struct tab_table *t = tab_create (1, 1);
307   tab_text (t, 0, 0, TAB_LEFT, text);
308   return &t->table;
309 }
310 \f
311 const char *
312 table_halign_to_string (enum table_halign halign)
313 {
314   switch (halign)
315     {
316     case TABLE_HALIGN_LEFT: return "left";
317     case TABLE_HALIGN_CENTER: return "center";
318     case TABLE_HALIGN_RIGHT: return "right";
319     case TABLE_HALIGN_DECIMAL: return "decimal";
320     case TABLE_HALIGN_MIXED: return "mixed";
321     default: return "**error**";
322     }
323 }
324
325 const char *
326 table_valign_to_string (enum table_valign valign)
327 {
328   switch (valign)
329     {
330     case TABLE_VALIGN_TOP: return "top";
331     case TABLE_VALIGN_CENTER: return "center";
332     case TABLE_VALIGN_BOTTOM: return "bottom";
333     default: return "**error**";
334     }
335 }
336
337 enum table_halign
338 table_halign_interpret (enum table_halign halign, bool numeric)
339 {
340   switch (halign)
341     {
342     case TABLE_HALIGN_LEFT:
343     case TABLE_HALIGN_CENTER:
344     case TABLE_HALIGN_RIGHT:
345       return halign;
346
347     case TABLE_HALIGN_MIXED:
348       return numeric ? TABLE_HALIGN_RIGHT : TABLE_HALIGN_LEFT;
349
350     case TABLE_HALIGN_DECIMAL:
351       return TABLE_HALIGN_DECIMAL;
352
353     default:
354       NOT_REACHED ();
355     }
356 }
357
358 void
359 font_style_copy (struct font_style *dst, const struct font_style *src)
360 {
361   *dst = *src;
362   if (dst->typeface)
363     dst->typeface = xstrdup (dst->typeface);
364 }
365
366 void
367 font_style_uninit (struct font_style *font)
368 {
369   if (font)
370     free (font->typeface);
371 }
372
373 void
374 area_style_copy (struct area_style *dst, const struct area_style *src)
375 {
376   font_style_copy (&dst->font_style, &src->font_style);
377   dst->cell_style = src->cell_style;
378 }
379
380 void
381 area_style_uninit (struct area_style *area)
382 {
383   if (area)
384     font_style_uninit (&area->font_style);
385 }
386
387 const char *
388 table_stroke_to_string (enum table_stroke stroke)
389 {
390   switch (stroke)
391     {
392     case TABLE_STROKE_NONE: return "none";
393     case TABLE_STROKE_SOLID: return "solid";
394     case TABLE_STROKE_DASHED: return "dashed";
395     case TABLE_STROKE_THICK: return "thick";
396     case TABLE_STROKE_THIN: return "thin";
397     case TABLE_STROKE_DOUBLE: return "double";
398     default:
399       return "**error**";
400     }
401 }
402
403 void
404 cell_color_dump (const struct cell_color *c)
405 {
406   if (c->alpha != 255)
407     printf ("rgba(%d, %d, %d, %d)", c->r, c->g, c->b, c->alpha);
408   else
409     printf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8, c->r, c->g, c->b);
410 }
411
412 void
413 font_style_dump (const struct font_style *f)
414 {
415   printf ("%s %dpx ", f->typeface, f->size);
416   cell_color_dump (&f->fg[0]);
417   putchar ('/');
418   cell_color_dump (&f->bg[0]);
419   if (!cell_color_equal (&f->fg[0], &f->fg[1])
420       || !cell_color_equal (&f->bg[0], &f->bg[1]))
421     {
422       printf (" alt=");
423       cell_color_dump (&f->fg[1]);
424       putchar ('/');
425       cell_color_dump (&f->bg[1]);
426     }
427   if (f->bold)
428     fputs (" bold", stdout);
429   if (f->italic)
430     fputs (" italic", stdout);
431   if (f->underline)
432     fputs (" underline", stdout);
433 }
434
435 void
436 cell_style_dump (const struct cell_style *c)
437 {
438   fputs (table_halign_to_string (c->halign), stdout);
439   if (c->halign == TABLE_HALIGN_DECIMAL)
440     printf ("(%.2gpx)", c->decimal_offset);
441   printf (" %s", table_valign_to_string (c->valign));
442   printf (" %d,%d,%d,%dpx",
443           c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1],
444           c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]);
445 }