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