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