table: Require headers to be set at table creation.
[pspp] / src / output / tab.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2013, 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/tab.h"
20
21 #include <ctype.h>
22 #include <stdarg.h>
23 #include <limits.h>
24 #include <stdlib.h>
25
26 #include "data/data-out.h"
27 #include "data/format.h"
28 #include "data/settings.h"
29 #include "data/value.h"
30 #include "data/variable.h"
31 #include "libpspp/assertion.h"
32 #include "libpspp/compiler.h"
33 #include "libpspp/i18n.h"
34 #include "libpspp/misc.h"
35 #include "libpspp/pool.h"
36 #include "output/driver.h"
37 #include "output/table-item.h"
38 #include "output/table-provider.h"
39 #include "output/text-item.h"
40
41 #include "gl/minmax.h"
42 #include "gl/xalloc.h"
43
44 #include "gettext.h"
45 #define _(msgid) gettext (msgid)
46 \f
47
48 static const bool debugging = true;
49
50
51 /* Cell options. */
52 #define TAB_JOIN     (1u << TAB_FIRST_AVAILABLE)
53
54 /* Joined cell. */
55 struct tab_joined_cell
56 {
57   int d[TABLE_N_AXES][2];       /* Table region, same as struct table_cell. */
58   char *text;
59
60   size_t n_footnotes;
61   const struct footnote **footnotes;
62
63   const struct area_style *style;
64 };
65
66 /* Creates and returns a new table with NC columns and NR rows and initially no
67    header rows or columns.
68
69    Sets the number of header rows on each side of TABLE to HL on the
70    left, HR on the right, HT on the top, HB on the bottom.  Header rows
71    are repeated when a table is broken across multiple columns or
72    multiple pages.
73
74    The table's cells are initially empty. */
75 struct tab_table *
76 tab_create (int nc, int nr, int hl, int hr, int ht, int hb)
77 {
78   struct tab_table *t;
79
80   t = pool_create_container (struct tab_table, container);
81   t->table.n[TABLE_HORZ] = nc;
82   t->table.n[TABLE_VERT] = nr;
83   t->table.h[TABLE_HORZ][0] = hl;
84   t->table.h[TABLE_HORZ][1] = hr;
85   t->table.h[TABLE_VERT][0] = ht;
86   t->table.h[TABLE_VERT][1] = hb;
87   t->table.ref_cnt = 1;
88
89   t->cc = pool_calloc (t->container, nr * nc, sizeof *t->cc);
90   t->ct = pool_calloc (t->container, nr * nc, sizeof *t->ct);
91
92   t->rh = pool_nmalloc (t->container, nc, nr + 1);
93   memset (t->rh, TAL_0, nc * (nr + 1));
94
95   t->rv = pool_nmalloc (t->container, nr, nc + 1);
96   memset (t->rv, TAL_0, nr * (nc + 1));
97
98   memset (t->styles, 0, sizeof t->styles);
99   memset (t->rule_colors, 0, sizeof t->rule_colors);
100
101   return t;
102 }
103 \f
104 /* Rules. */
105
106 /* Draws a vertical line to the left of cells at horizontal position X
107    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
108 void
109 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
110 {
111   if (debugging)
112     {
113       if (x < 0 || x > tab_nc (t)
114           || y1 < 0 || y1 >= tab_nr (t)
115           || y2 < 0 || y2 >= tab_nr (t))
116         {
117           printf (_("bad vline: x=%d y=(%d,%d) in table size (%d,%d)\n"),
118                   x, y1, y2, tab_nc (t), tab_nr (t));
119           return;
120         }
121     }
122
123   assert (x >= 0);
124   assert (x <= tab_nc (t));
125   assert (y1 >= 0);
126   assert (y2 >= y1);
127   assert (y2 <= tab_nr (t));
128
129   if (style != -1)
130     {
131       int y;
132       for (y = y1; y <= y2; y++)
133         t->rv[x + (tab_nc (t) + 1) * y] = style;
134     }
135 }
136
137 /* Draws a horizontal line above cells at vertical position Y from X1
138    to X2 inclusive in style STYLE, if style is not -1. */
139 void
140 tab_hline (struct tab_table *t, int style, int x1, int x2, int y)
141 {
142   if (debugging)
143     {
144       if (y < 0 || y > tab_nr (t)
145           || x1 < 0 || x1 >= tab_nc (t)
146           || x2 < 0 || x2 >= tab_nc (t))
147         {
148           printf (_("bad hline: x=(%d,%d) y=%d in table size (%d,%d)\n"),
149                   x1, x2, y, tab_nc (t), tab_nr (t));
150           return;
151         }
152     }
153
154   assert (y >= 0);
155   assert (y <= tab_nr (t));
156   assert (x2 >= x1);
157   assert (x1 >= 0);
158   assert (x2 < tab_nc (t));
159
160   if (style != -1)
161     {
162       int x;
163       for (x = x1; x <= x2; x++)
164         t->rh[x + tab_nc (t) * y] = style;
165     }
166 }
167
168 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
169    lines of style F_H and vertical lines of style F_V.  Fills the
170    interior of the box with horizontal lines of style I_H and vertical
171    lines of style I_V.  Any of the line styles may be -1 to avoid
172    drawing those lines.  This is distinct from 0, which draws a null
173    line. */
174 void
175 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
176          int x1, int y1, int x2, int y2)
177 {
178   if (debugging)
179     {
180       if (x1 < 0 || x1 >= tab_nc (t)
181           || x2 < 0 || x2 >= tab_nc (t)
182           || y1 < 0 || y1 >= tab_nr (t)
183           || y2 < 0 || y2 >= tab_nr (t))
184         {
185           printf (_("bad box: (%d,%d)-(%d,%d) in table size (%d,%d)\n"),
186                   x1, y1, x2, y2, tab_nc (t), tab_nr (t));
187           NOT_REACHED ();
188         }
189     }
190
191   assert (x2 >= x1);
192   assert (y2 >= y1);
193   assert (x1 >= 0);
194   assert (y1 >= 0);
195   assert (x2 < tab_nc (t));
196   assert (y2 < tab_nr (t));
197
198   if (f_h != -1)
199     {
200       int x;
201       for (x = x1; x <= x2; x++)
202         {
203           t->rh[x + tab_nc (t) * y1] = f_h;
204           t->rh[x + tab_nc (t) * (y2 + 1)] = f_h;
205         }
206     }
207   if (f_v != -1)
208     {
209       int y;
210       for (y = y1; y <= y2; y++)
211         {
212           t->rv[x1 + (tab_nc (t) + 1) * y] = f_v;
213           t->rv[(x2 + 1) + (tab_nc (t) + 1) * y] = f_v;
214         }
215     }
216
217   if (i_h != -1)
218     {
219       int y;
220
221       for (y = y1 + 1; y <= y2; y++)
222         {
223           int x;
224
225           for (x = x1; x <= x2; x++)
226             t->rh[x + tab_nc (t) * y] = i_h;
227         }
228     }
229   if (i_v != -1)
230     {
231       int x;
232
233       for (x = x1 + 1; x <= x2; x++)
234         {
235           int y;
236
237           for (y = y1; y <= y2; y++)
238             t->rv[x + (tab_nc (t) + 1) * y] = i_v;
239         }
240     }
241 }
242 \f
243 /* Cells. */
244
245 static void
246 do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
247 {
248   assert (c >= 0);
249   assert (r >= 0);
250   assert (c < tab_nc (table));
251   assert (r < tab_nr (table));
252
253   if (debugging)
254     {
255       if (c < 0 || r < 0 || c >= tab_nc (table) || r >= tab_nr (table))
256         {
257           printf ("tab_text(): bad cell (%d,%d) in table size (%d,%d)\n",
258                   c, r, tab_nc (table), tab_nr (table));
259           return;
260         }
261     }
262
263   table->cc[c + r * tab_nc (table)] = text;
264   table->ct[c + r * tab_nc (table)] = opt;
265 }
266
267 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
268    TEXT. */
269 void
270 tab_text (struct tab_table *table, int c, int r, unsigned opt,
271           const char *text)
272 {
273   do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
274 }
275
276 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
277    FORMAT, which is formatted as if passed to printf. */
278 void
279 tab_text_format (struct tab_table *table, int c, int r, unsigned opt,
280                  const char *format, ...)
281 {
282   va_list args;
283
284   va_start (args, format);
285   do_tab_text (table, c, r, opt,
286                pool_vasprintf (table->container, format, args));
287   va_end (args);
288 }
289
290 static struct tab_joined_cell *
291 add_joined_cell (struct tab_table *table, int x1, int y1, int x2, int y2,
292                  unsigned opt)
293 {
294   struct tab_joined_cell *j;
295
296   assert (x1 >= 0);
297   assert (y1 >= 0);
298   assert (y2 >= y1);
299   assert (x2 >= x1);
300   assert (y2 < tab_nr (table));
301   assert (x2 < tab_nc (table));
302
303   if (debugging)
304     {
305       if (x1 < 0 || x1 >= tab_nc (table)
306           || y1 < 0 || y1 >= tab_nr (table)
307           || x2 < x1 || x2 >= tab_nc (table)
308           || y2 < y1 || y2 >= tab_nr (table))
309         {
310           printf ("tab_joint_text(): bad cell "
311                   "(%d,%d)-(%d,%d) in table size (%d,%d)\n",
312                   x1, y1, x2, y2, tab_nc (table), tab_nr (table));
313           return NULL;
314         }
315     }
316
317   tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
318
319   j = pool_alloc (table->container, sizeof *j);
320   j->d[TABLE_HORZ][0] = x1;
321   j->d[TABLE_VERT][0] = y1;
322   j->d[TABLE_HORZ][1] = ++x2;
323   j->d[TABLE_VERT][1] = ++y2;
324   j->n_footnotes = 0;
325   j->footnotes = NULL;
326   j->style = NULL;
327
328   {
329     void **cc = &table->cc[x1 + y1 * tab_nc (table)];
330     unsigned short *ct = &table->ct[x1 + y1 * tab_nc (table)];
331     const int ofs = tab_nc (table) - (x2 - x1);
332
333     int y;
334
335     for (y = y1; y < y2; y++)
336       {
337         int x;
338
339         for (x = x1; x < x2; x++)
340           {
341             *cc++ = j;
342             *ct++ = opt | TAB_JOIN;
343           }
344
345         cc += ofs;
346         ct += ofs;
347       }
348   }
349
350   return j;
351 }
352
353 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
354    options OPT to have text value TEXT. */
355 void
356 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
357                 unsigned opt, const char *text)
358 {
359   char *s = pool_strdup (table->container, text);
360   if (x1 == x2 && y1 == y2)
361     do_tab_text (table, x1, y1, opt, s);
362   else
363     add_joined_cell (table, x1, y1, x2, y2, opt)->text = s;
364 }
365
366 struct footnote *
367 tab_create_footnote (struct tab_table *table, size_t idx, const char *content,
368                      const char *marker, struct area_style *style)
369 {
370   struct footnote *f = pool_alloc (table->container, sizeof *f);
371   f->idx = idx;
372   f->content = pool_strdup (table->container, content);
373   f->marker = pool_strdup (table->container, marker);
374   f->style = style;
375   return f;
376 }
377
378 void
379 tab_add_footnote (struct tab_table *table, int x, int y,
380                   const struct footnote *f)
381 {
382   int index = x + y * tab_nc (table);
383   unsigned short opt = table->ct[index];
384   struct tab_joined_cell *j;
385
386   if (opt & TAB_JOIN)
387     j = table->cc[index];
388   else
389     {
390       char *text = table->cc[index];
391
392       j = add_joined_cell (table, x, y, x, y, table->ct[index]);
393       j->text = text ? text : xstrdup ("");
394     }
395
396   j->footnotes = pool_realloc (table->container, j->footnotes,
397                                (j->n_footnotes + 1) * sizeof *j->footnotes);
398
399   j->footnotes[j->n_footnotes++] = f;
400 }
401
402 void
403 tab_add_style (struct tab_table *table, int x, int y,
404                const struct area_style *style)
405 {
406   int index = x + y * tab_nc (table);
407   unsigned short opt = table->ct[index];
408   struct tab_joined_cell *j;
409
410   if (opt & TAB_JOIN)
411     j = table->cc[index];
412   else
413     {
414       char *text = table->cc[index];
415
416       j = add_joined_cell (table, x, y, x, y, table->ct[index]);
417       j->text = text ? text : xstrdup ("");
418     }
419
420   j->style = style;
421 }
422
423 bool
424 tab_cell_is_empty (const struct tab_table *table, int c, int r)
425 {
426   return table->cc[c + r * tab_nc (table)] == NULL;
427 }
428 \f
429 /* Editing. */
430
431 /* Writes STRING to the output.  OPTIONS may be any valid combination of TAB_*
432    bits.
433
434    This function is obsolete.  Please do not add new uses of it.  Instead, use
435    a text_item (see output/text-item.h). */
436 void
437 tab_output_text (int options UNUSED, const char *string)
438 {
439   text_item_submit (text_item_create (TEXT_ITEM_LOG, string));
440 }
441
442 /* Same as tab_output_text(), but FORMAT is passed through printf-like
443    formatting before output. */
444 void
445 tab_output_text_format (int options, const char *format, ...)
446 {
447   va_list args;
448   char *text;
449
450   va_start (args, format);
451   text = xvasprintf (format, args);
452   va_end (args);
453
454   tab_output_text (options, text);
455
456   free (text);
457 }
458 \f
459 /* Table class implementation. */
460
461 void
462 tab_destroy (struct table *table)
463 {
464   struct tab_table *t = tab_cast (table);
465   pool_destroy (t->container);
466 }
467
468 void
469 tab_get_cell (const struct table *table, int x, int y,
470               struct table_cell *cell)
471 {
472   const struct tab_table *t = tab_cast (table);
473   int index = x + y * tab_nc (t);
474   unsigned short opt = t->ct[index];
475   const void *cc = t->cc[index];
476
477   cell->options = opt;
478   cell->n_footnotes = 0;
479
480   int style_idx = (opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT;
481   const struct area_style *style = t->styles[style_idx];
482   if (style)
483     cell->style = style;
484   else
485     {
486       static const struct area_style styles[3][3] = {
487 #define S(H,V) [H][V] = { AREA_STYLE_INITIALIZER__,     \
488                           .cell_style.halign = H,       \
489                           .cell_style.valign = V }
490         S(TABLE_HALIGN_LEFT, TABLE_VALIGN_TOP),
491         S(TABLE_HALIGN_LEFT, TABLE_VALIGN_CENTER),
492         S(TABLE_HALIGN_LEFT, TABLE_VALIGN_BOTTOM),
493         S(TABLE_HALIGN_CENTER, TABLE_VALIGN_TOP),
494         S(TABLE_HALIGN_CENTER, TABLE_VALIGN_CENTER),
495         S(TABLE_HALIGN_CENTER, TABLE_VALIGN_BOTTOM),
496         S(TABLE_HALIGN_RIGHT, TABLE_VALIGN_TOP),
497         S(TABLE_HALIGN_RIGHT, TABLE_VALIGN_CENTER),
498         S(TABLE_HALIGN_RIGHT, TABLE_VALIGN_BOTTOM),
499       };
500
501       enum table_halign halign
502         = ((opt & TAB_HALIGN) == TAB_LEFT ? TABLE_HALIGN_LEFT
503            : (opt & TAB_HALIGN) == TAB_CENTER ? TABLE_HALIGN_CENTER
504            : TABLE_HALIGN_RIGHT);
505       enum table_valign valign
506         = ((opt & TAB_VALIGN) == TAB_TOP ? TABLE_VALIGN_TOP
507            : (opt & TAB_VALIGN) == TAB_MIDDLE ? TABLE_VALIGN_CENTER
508            : TABLE_VALIGN_BOTTOM);
509
510       cell->style = &styles[halign][valign];
511     }
512
513   if (opt & TAB_JOIN)
514     {
515       const struct tab_joined_cell *jc = cc;
516       cell->text = jc->text;
517
518       cell->footnotes = jc->footnotes;
519       cell->n_footnotes = jc->n_footnotes;
520
521       cell->d[TABLE_HORZ][0] = jc->d[TABLE_HORZ][0];
522       cell->d[TABLE_HORZ][1] = jc->d[TABLE_HORZ][1];
523       cell->d[TABLE_VERT][0] = jc->d[TABLE_VERT][0];
524       cell->d[TABLE_VERT][1] = jc->d[TABLE_VERT][1];
525
526       if (jc->style)
527         cell->style = jc->style;
528     }
529   else
530     {
531       cell->d[TABLE_HORZ][0] = x;
532       cell->d[TABLE_HORZ][1] = x + 1;
533       cell->d[TABLE_VERT][0] = y;
534       cell->d[TABLE_VERT][1] = y + 1;
535       cell->text = CONST_CAST (char *, cc ? cc : "");
536     }
537 }
538
539 int
540 tab_get_rule (const struct table *table, enum table_axis axis, int x, int y,
541               struct cell_color *color)
542 {
543   const struct tab_table *t = tab_cast (table);
544   uint8_t raw = (axis == TABLE_VERT
545                  ? t->rh[x + tab_nc (t) * y]
546                  : t->rv[x + (tab_nc (t) + 1) * y]);
547   struct cell_color *p = t->rule_colors[(raw & TAB_RULE_STYLE_MASK)
548                                         >> TAB_RULE_STYLE_SHIFT];
549   if (p)
550     *color = *p;
551   return (raw & TAB_RULE_TYPE_MASK) >> TAB_RULE_TYPE_SHIFT;
552 }
553
554 struct tab_table *
555 tab_cast (const struct table *table)
556 {
557   return UP_CAST (table, struct tab_table, table);
558 }