output: Implement styling for titles and captions.
[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 <stdlib.h>
24
25 #include "libpspp/cast.h"
26 #include "libpspp/compiler.h"
27 #include "libpspp/pool.h"
28 #include "libpspp/str.h"
29 #include "output/table-item.h"
30
31 #include "gl/xalloc.h"
32
33 /* Increases TABLE's reference count, indicating that it has an additional
34    owner.  An table that is shared among multiple owners must not be
35    modified. */
36 struct table *
37 table_ref (const struct table *table_)
38 {
39   struct table *table = CONST_CAST (struct table *, table_);
40   table->ref_cnt++;
41   return table;
42 }
43
44 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
45    If TABLE no longer has any owners, it is freed. */
46 void
47 table_unref (struct table *table)
48 {
49   if (table != NULL)
50     {
51       assert (table->ref_cnt > 0);
52       if (--table->ref_cnt == 0)
53         table->klass->destroy (table);
54     }
55 }
56
57 /* Returns true if TABLE has more than one owner.  A table item that is shared
58    among multiple owners must not be modified. */
59 bool
60 table_is_shared (const struct table *table)
61 {
62   return table->ref_cnt > 1;
63 }
64
65 /* Sets the number of left header columns in TABLE to HL. */
66 void
67 table_set_hl (struct table *table, int hl)
68 {
69   assert (!table_is_shared (table));
70   table->h[TABLE_HORZ][0] = hl;
71 }
72
73 /* Sets the number of right header columns in TABLE to HR. */
74 void
75 table_set_hr (struct table *table, int hr)
76 {
77   assert (!table_is_shared (table));
78   table->h[TABLE_HORZ][1] = hr;
79 }
80
81 /* Sets the number of top header rows in TABLE to HT. */
82 void
83 table_set_ht (struct table *table, int ht)
84 {
85   assert (!table_is_shared (table));
86   table->h[TABLE_VERT][0] = ht;
87 }
88
89 /* Sets the number of top header rows in TABLE to HB. */
90 void
91 table_set_hb (struct table *table, int hb)
92 {
93   assert (!table_is_shared (table));
94   table->h[TABLE_VERT][1] = hb;
95 }
96 \f
97 /* Initializes TABLE as a table of the specified CLASS, initially with a
98    reference count of 1.
99
100    TABLE initially has 0 rows and columns and no headers.  The table
101    implementation should update the numbers of rows and columns.  The table
102    implementation (or its client) may update the header rows and columns.
103
104    A table is an abstract class, that is, a plain struct table is not useful on
105    its own.  Thus, this function is normally called from the initialization
106    function of some subclass of table. */
107 void
108 table_init (struct table *table, const struct table_class *class)
109 {
110   table->klass = class;
111   table->n[TABLE_HORZ] = table->n[TABLE_VERT] = 0;
112   table->h[TABLE_HORZ][0] = table->h[TABLE_HORZ][1] = 0;
113   table->h[TABLE_VERT][0] = table->h[TABLE_VERT][1] = 0;
114   table->ref_cnt = 1;
115 }
116
117 /* Sets the number of columns in TABLE to NC. */
118 void
119 table_set_nc (struct table *table, int nc)
120 {
121   assert (!table_is_shared (table));
122   table->n[TABLE_HORZ] = nc;
123 }
124
125 /* Sets the number of rows in TABLE to NR. */
126 void
127 table_set_nr (struct table *table, int nr)
128 {
129   assert (!table_is_shared (table));
130   table->n[TABLE_VERT] = nr;
131 }
132 \f
133 struct cell_style *
134 cell_style_clone (struct pool *pool, const struct cell_style *old)
135 {
136   struct cell_style *new = pool_malloc (pool, sizeof *new);
137   *new = *old;
138   if (new->font)
139     new->font = pool_strdup (pool, new->font);
140   return new;
141 }
142
143 void
144 cell_style_free (struct cell_style *style)
145 {
146   if (style)
147     {
148       free (style->font);
149       free (style);
150     }
151 }
152
153 /* Initializes CELL with the contents of the table cell at column X and row Y
154    within TABLE.  When CELL is no longer needed, the caller is responsible for
155    freeing it by calling table_cell_free(CELL).
156
157    The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
158 void
159 table_get_cell (const struct table *table, int x, int y,
160                 struct table_cell *cell)
161 {
162   assert (x >= 0 && x < table->n[TABLE_HORZ]);
163   assert (y >= 0 && y < table->n[TABLE_VERT]);
164
165   static const struct cell_style default_style = CELL_STYLE_INITIALIZER;
166   cell->style = &default_style;
167
168   table->klass->get_cell (table, x, y, cell);
169 }
170
171 /* Frees CELL, which should have been initialized by calling
172    table_get_cell(). */
173 void
174 table_cell_free (struct table_cell *cell)
175 {
176   if (cell->destructor != NULL)
177     cell->destructor (cell->destructor_aux);
178 }
179
180 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
181    representing a rule running alongside one of the cells in TABLE.
182
183    Suppose NC is the number of columns in TABLE and NR is the number of rows.
184    Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
185    (0,0), the return value is the rule that runs vertically on the left side of
186    cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
187    cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
188    cell (NC-1,0).
189
190    The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
191    within a 7x7 table.  The '|' characters at the intersection of the X labels
192    and Y labels show the rule whose style would be returned by calling
193    table_get_rule with those X and Y values:
194
195                            0  1  2  3  4  5  6  7
196                            +--+--+--+--+--+--+--+
197                          0 |  |  |  |  |  |  |  |
198                            +--+--+--+--+--+--+--+
199                          1 |  |  |  |  |  |  |  |
200                            +--+--+--+--+--+--+--+
201                          2 |  |  |  |  |  |  |  |
202                            +--+--+--+--+--+--+--+
203                          3 |  |  |  |  |  |  |  |
204                            +--+--+--+--+--+--+--+
205                          4 |  |  |  |  |  |  |  |
206                            +--+--+--+--+--+--+--+
207                          5 |  |  |  |  |  |  |  |
208                            +--+--+--+--+--+--+--+
209                          6 |  |  |  |  |  |  |  |
210                            +--+--+--+--+--+--+--+
211
212    Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
213    (X,Y) = (0,0), the return value is the rule that runs horizontally above
214    the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
215    between that cell and cell (0,1); and so on, up to (0,NR), which runs
216    horizontally below cell (0,NR-1). */
217 int
218 table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
219                 struct cell_color *color)
220 {
221   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
222   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
223   *color = CELL_COLOR_BLACK;
224   return table->klass->get_rule (table, axis, x, y, color);
225 }
226
227 void
228 cell_contents_format_footnote_markers (const struct cell_contents *c,
229                                        struct string *s)
230 {
231   for (size_t i = 0; i < c->n_footnotes; i++)
232     {
233       if (i)
234         ds_put_byte (s, ',');
235       ds_put_cstr (s, c->footnotes[i]->marker);
236     }
237 }
238
239 static const struct footnote **
240 add_footnotes (const struct footnote **refs, size_t n_refs,
241                const struct footnote **footnotes, size_t *allocated, size_t *n)
242 {
243   for (size_t i = 0; i < n_refs; i++)
244     {
245       const struct footnote *f = refs[i];
246       if (f->idx >= *allocated)
247         {
248           size_t new_allocated = (f->idx + 1) * 2;
249           footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
250           while (*allocated < new_allocated)
251             footnotes[(*allocated)++] = NULL;
252         }
253       footnotes[f->idx] = f;
254       if (f->idx >= *n)
255         *n = f->idx + 1;
256     }
257   return footnotes;
258 }
259
260 size_t
261 table_collect_footnotes (const struct table_item *item,
262                          const struct footnote ***footnotesp)
263 {
264   const struct footnote **footnotes = NULL;
265   size_t allocated = 0;
266   size_t n = 0;
267
268   struct table *t = item->table;
269   for (int y = 0; y < table_nr (t); y++)
270     {
271       struct table_cell cell;
272       for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
273         {
274           table_get_cell (t, x, y, &cell);
275
276           if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
277             for (size_t i = 0; i < cell.n_contents; i++)
278               {
279                 const struct cell_contents *c = &cell.contents[i];
280                 footnotes = add_footnotes (c->footnotes, c->n_footnotes,
281                                            footnotes, &allocated, &n);
282               }
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 struct table_unshared
302   {
303     struct table table;
304     struct table *subtable;
305   };
306
307 static const struct table_class table_unshared_class;
308
309 /* Takes ownership of TABLE and returns a table with the same contents but
310    which is guaranteed not to be shared (as returned by table_is_shared()).
311
312    If TABLE is unshared, just returns TABLE.
313
314    The only real use for this function is to create a copy of TABLE in which
315    the headers can be adjusted, which is a pretty specialized use case. */
316 struct table *
317 table_unshare (struct table *table)
318 {
319   if (!table_is_shared (table))
320     return table;
321   else
322     {
323       struct table_unshared *tiu = xmalloc (sizeof *tiu);
324       table_init (&tiu->table, &table_unshared_class);
325       table_set_nc (&tiu->table, table_nc (table));
326       table_set_nr (&tiu->table, table_nr (table));
327       table_set_hl (&tiu->table, table_hl (table));
328       table_set_hr (&tiu->table, table_hr (table));
329       table_set_ht (&tiu->table, table_ht (table));
330       table_set_hb (&tiu->table, table_hb (table));
331       tiu->subtable = table;
332       return &tiu->table;
333     }
334 }
335
336 static struct table_unshared *
337 table_unshared_cast (const struct table *table)
338 {
339   assert (table->klass == &table_unshared_class);
340   return UP_CAST (table, struct table_unshared, table);
341 }
342
343 static void
344 table_unshared_destroy (struct table *tiu_)
345 {
346   struct table_unshared *tiu = table_unshared_cast (tiu_);
347   table_unref (tiu->subtable);
348   free (tiu);
349 }
350
351 static void
352 table_unshared_get_cell (const struct table *tiu_, int x, int y,
353                               struct table_cell *cell)
354 {
355   struct table_unshared *tiu = table_unshared_cast (tiu_);
356   table_get_cell (tiu->subtable, x, y, cell);
357 }
358
359 static int
360 table_unshared_get_rule (const struct table *tiu_,
361                          enum table_axis axis, int x, int y,
362                          struct cell_color *color)
363 {
364   struct table_unshared *tiu = table_unshared_cast (tiu_);
365   return table_get_rule (tiu->subtable, axis, x, y, color);
366 }
367
368 static const struct table_class table_unshared_class =
369   {
370     table_unshared_destroy,
371     table_unshared_get_cell,
372     table_unshared_get_rule,
373     NULL,                       /* paste */
374     NULL,                       /* select */
375   };
376 \f
377 struct table_string
378   {
379     struct table table;
380     char *string;
381     unsigned int options;
382   };
383
384 static const struct table_class table_string_class;
385
386 /* Returns a table that contains a single cell, whose contents are S with
387    options OPTIONS (a combination of TAB_* values).  */
388 struct table *
389 table_from_string (unsigned int options, const char *s)
390 {
391   struct table_string *ts = xmalloc (sizeof *ts);
392   table_init (&ts->table, &table_string_class);
393   ts->table.n[TABLE_HORZ] = ts->table.n[TABLE_VERT] = 1;
394   ts->string = xstrdup (s);
395   ts->options = options;
396   return &ts->table;
397 }
398
399 static struct table_string *
400 table_string_cast (const struct table *table)
401 {
402   assert (table->klass == &table_string_class);
403   return UP_CAST (table, struct table_string, table);
404 }
405
406 static void
407 table_string_destroy (struct table *ts_)
408 {
409   struct table_string *ts = table_string_cast (ts_);
410   free (ts->string);
411   free (ts);
412 }
413
414 static void
415 table_string_get_cell (const struct table *ts_, int x UNUSED, int y UNUSED,
416                        struct table_cell *cell)
417 {
418   struct table_string *ts = table_string_cast (ts_);
419   cell->d[TABLE_HORZ][0] = 0;
420   cell->d[TABLE_HORZ][1] = 1;
421   cell->d[TABLE_VERT][0] = 0;
422   cell->d[TABLE_VERT][1] = 1;
423   cell->contents = &cell->inline_contents;
424   cell->inline_contents.options = ts->options;
425   cell->inline_contents.text = ts->string;
426   cell->inline_contents.n_footnotes = 0;
427   cell->n_contents = 1;
428   cell->destructor = NULL;
429 }
430
431
432 static int
433 table_string_get_rule (const struct table *ts UNUSED,
434                        enum table_axis axis UNUSED, int x UNUSED, int y UNUSED,
435                        struct cell_color *color UNUSED)
436 {
437   return TAL_0;
438 }
439
440 static const struct table_class table_string_class =
441   {
442     table_string_destroy,
443     table_string_get_cell,
444     table_string_get_rule,
445     NULL,                       /* paste */
446     NULL,                       /* select */
447   };