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