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