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