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