db8488d8546329339e56c57d213b7d59b8831218
[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   union
59   {
60     char *text;
61     struct table_item *subtable;
62   }
63     u;
64
65   size_t n_footnotes;
66   const struct footnote **footnotes;
67
68   const struct cell_style *style;
69 };
70
71 static const struct table_class tab_table_class;
72
73 struct fmt_spec ugly[n_RC] = {
74   {FMT_F, 8, 0},                /* INTEGER */
75   {FMT_F, 8, 3},                /* WEIGHT (ignored) */
76   {FMT_F, 8, 3},                /* PVALUE */
77   {FMT_F, 8, 3}                 /* OTHER (ignored) */
78 };
79
80
81 /* Creates and returns a new table with NC columns and NR rows and initially no
82    header rows or columns.  The table's cells are initially empty. */
83 struct tab_table *
84 tab_create (int nc, int nr)
85 {
86   struct tab_table *t;
87
88   t = pool_create_container (struct tab_table, container);
89   table_init (&t->table, &tab_table_class);
90   table_set_nc (&t->table, nc);
91   table_set_nr (&t->table, nr);
92
93   t->title = NULL;
94   t->caption = NULL;
95   t->cf = nc;
96   t->cc = pool_calloc (t->container, nr * nc, sizeof *t->cc);
97   t->ct = pool_calloc (t->container, nr * nc, sizeof *t->ct);
98
99   t->rh = pool_nmalloc (t->container, nc, nr + 1);
100   memset (t->rh, TAL_0, nc * (nr + 1));
101
102   t->rv = pool_nmalloc (t->container, nr, nc + 1);
103   memset (t->rv, TAL_0, nr * (nc + 1));
104
105   memset (t->fmtmap, 0, sizeof (*t->fmtmap) * n_RC);
106   t->fmtmap[RC_PVALUE] = ugly[RC_PVALUE];
107   t->fmtmap[RC_INTEGER] = ugly[RC_INTEGER];
108   t->fmtmap[RC_OTHER] = *settings_get_format ();
109
110   memset (t->styles, 0, sizeof t->styles);
111   memset (t->rule_colors, 0, sizeof t->rule_colors);
112
113   t->col_ofs = t->row_ofs = 0;
114
115   return t;
116 }
117
118
119 void
120 tab_set_format (struct tab_table *t, enum result_class rc,
121                 const struct fmt_spec *fmt)
122 {
123   t->fmtmap[rc] = *fmt;
124 }
125
126
127 /* Sets the width and height of a table, in columns and rows,
128    respectively.  Use only to reduce the size of a table, since it
129    does not change the amount of allocated memory.
130
131    This function is obsolete.  Please do not add new uses of it.  (Instead, use
132    table_select() or one of its helper functions.) */
133 void
134 tab_resize (struct tab_table *t, int nc, int nr)
135 {
136   if (nc != -1)
137     {
138       assert (nc + t->col_ofs <= t->cf);
139       table_set_nc (&t->table, nc + t->col_ofs);
140     }
141   if (nr != -1)
142     {
143       assert (nr + t->row_ofs <= tab_nr (t));
144       table_set_nr (&t->table, nr + t->row_ofs);
145     }
146 }
147
148 /* Changes either or both dimensions of a table and reallocates memory as
149    necessary.
150
151    This function is obsolete.  Please do not add new uses of it.  (Instead, use
152    table_paste() or one of its helper functions to paste multiple tables
153    together into a larger one.) */
154 void
155 tab_realloc (struct tab_table *t, int nc, int nr)
156 {
157   int ro, co;
158
159   ro = t->row_ofs;
160   co = t->col_ofs;
161   if (ro || co)
162     tab_offset (t, 0, 0);
163
164   if (nc == -1)
165     nc = tab_nc (t);
166   if (nr == -1)
167     nr = tab_nr (t);
168
169   assert (nc == tab_nc (t));
170
171   if (nc > t->cf)
172     {
173       int mr1 = MIN (nr, tab_nr (t));
174       int mc1 = MIN (nc, tab_nc (t));
175
176       void **new_cc;
177       unsigned short *new_ct;
178       int r;
179
180       new_cc = pool_calloc (t->container, nr * nc, sizeof *new_cc);
181       new_ct = pool_malloc (t->container, nr * nc);
182       for (r = 0; r < mr1; r++)
183         {
184           memcpy (&new_cc[r * nc], &t->cc[r * tab_nc (t)],
185                   mc1 * sizeof *t->cc);
186           memcpy (&new_ct[r * nc], &t->ct[r * tab_nc (t)],
187                   mc1 * sizeof *t->ct);
188           memset (&new_ct[r * nc + tab_nc (t)], 0, nc - tab_nc (t));
189         }
190       pool_free (t->container, t->cc);
191       pool_free (t->container, t->ct);
192       t->cc = new_cc;
193       t->ct = new_ct;
194       t->cf = nc;
195     }
196   else if (nr != tab_nr (t))
197     {
198       t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
199       t->ct = pool_nrealloc (t->container, t->ct, nr * nc, sizeof *t->ct);
200
201       t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
202       t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
203
204       if (nr > tab_nr (t))
205         {
206           memset (&t->rh[nc * (tab_nr (t) + 1)], TAL_0,
207                   (nr - tab_nr (t)) * nc);
208           memset (&t->rv[(nc + 1) * tab_nr (t)], TAL_0,
209                   (nr - tab_nr (t)) * (nc + 1));
210         }
211     }
212
213   memset (&t->ct[nc * tab_nr (t)], 0, nc * (nr - tab_nr (t)) * sizeof *t->ct);
214   memset (&t->cc[nc * tab_nr (t)], 0, nc * (nr - tab_nr (t)) * sizeof *t->cc);
215
216   table_set_nr (&t->table, nr);
217   table_set_nc (&t->table, nc);
218
219   if (ro || co)
220     tab_offset (t, co, ro);
221 }
222
223 /* Sets the number of header rows on each side of TABLE to L on the
224    left, R on the right, T on the top, B on the bottom.  Header rows
225    are repeated when a table is broken across multiple columns or
226    multiple pages. */
227 void
228 tab_headers (struct tab_table *table, int l, int r, int t, int b)
229 {
230   table_set_hl (&table->table, l);
231   table_set_hr (&table->table, r);
232   table_set_ht (&table->table, t);
233   table_set_hb (&table->table, b);
234 }
235 \f
236 /* Rules. */
237
238 /* Draws a vertical line to the left of cells at horizontal position X
239    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
240 void
241 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
242 {
243   if (debugging)
244     {
245       if (x + t->col_ofs < 0 || x + t->col_ofs > tab_nc (t)
246           || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
247           || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
248         {
249           printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
250                     "table size (%d,%d)\n"),
251                   x, t->col_ofs, x + t->col_ofs,
252                   y1, t->row_ofs, y1 + t->row_ofs,
253                   y2, t->row_ofs, y2 + t->row_ofs, tab_nc (t), tab_nr (t));
254           return;
255         }
256     }
257
258   x += t->col_ofs;
259   y1 += t->row_ofs;
260   y2 += t->row_ofs;
261
262   assert (x >= 0);
263   assert (x <= tab_nc (t));
264   assert (y1 >= 0);
265   assert (y2 >= y1);
266   assert (y2 <= tab_nr (t));
267
268   if (style != -1)
269     {
270       int y;
271       for (y = y1; y <= y2; y++)
272         t->rv[x + (t->cf + 1) * y] = style;
273     }
274 }
275
276 /* Draws a horizontal line above cells at vertical position Y from X1
277    to X2 inclusive in style STYLE, if style is not -1. */
278 void
279 tab_hline (struct tab_table *t, int style, int x1, int x2, int y)
280 {
281   if (debugging)
282     {
283       if (y + t->row_ofs < 0 || y + t->row_ofs > tab_nr (t)
284           || x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t)
285           || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t))
286         {
287           printf (_("bad hline: x=(%d+%d=%d,%d+%d=%d) y=%d+%d=%d in "
288                     "table size (%d,%d)\n"),
289                   x1, t->col_ofs, x1 + t->col_ofs,
290                   x2, t->col_ofs, x2 + t->col_ofs,
291                   y, t->row_ofs, y + t->row_ofs, tab_nc (t), tab_nr (t));
292           return;
293         }
294     }
295
296   x1 += t->col_ofs;
297   x2 += t->col_ofs;
298   y += t->row_ofs;
299
300   assert (y >= 0);
301   assert (y <= tab_nr (t));
302   assert (x2 >= x1);
303   assert (x1 >= 0);
304   assert (x2 < tab_nc (t));
305
306   if (style != -1)
307     {
308       int x;
309       for (x = x1; x <= x2; x++)
310         t->rh[x + t->cf * y] = style;
311     }
312 }
313
314 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
315    lines of style F_H and vertical lines of style F_V.  Fills the
316    interior of the box with horizontal lines of style I_H and vertical
317    lines of style I_V.  Any of the line styles may be -1 to avoid
318    drawing those lines.  This is distinct from 0, which draws a null
319    line. */
320 void
321 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
322          int x1, int y1, int x2, int y2)
323 {
324   if (debugging)
325     {
326       if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t)
327           || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t)
328           || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
329           || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
330         {
331           printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
332                     "in table size (%d,%d)\n"),
333                   x1, t->col_ofs, x1 + t->col_ofs,
334                   y1, t->row_ofs, y1 + t->row_ofs,
335                   x2, t->col_ofs, x2 + t->col_ofs,
336                   y2, t->row_ofs, y2 + t->row_ofs, tab_nc (t), tab_nr (t));
337           NOT_REACHED ();
338         }
339     }
340
341   x1 += t->col_ofs;
342   x2 += t->col_ofs;
343   y1 += t->row_ofs;
344   y2 += t->row_ofs;
345
346   assert (x2 >= x1);
347   assert (y2 >= y1);
348   assert (x1 >= 0);
349   assert (y1 >= 0);
350   assert (x2 < tab_nc (t));
351   assert (y2 < tab_nr (t));
352
353   if (f_h != -1)
354     {
355       int x;
356       for (x = x1; x <= x2; x++)
357         {
358           t->rh[x + t->cf * y1] = f_h;
359           t->rh[x + t->cf * (y2 + 1)] = f_h;
360         }
361     }
362   if (f_v != -1)
363     {
364       int y;
365       for (y = y1; y <= y2; y++)
366         {
367           t->rv[x1 + (t->cf + 1) * y] = f_v;
368           t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
369         }
370     }
371
372   if (i_h != -1)
373     {
374       int y;
375
376       for (y = y1 + 1; y <= y2; y++)
377         {
378           int x;
379
380           for (x = x1; x <= x2; x++)
381             t->rh[x + t->cf * y] = i_h;
382         }
383     }
384   if (i_v != -1)
385     {
386       int x;
387
388       for (x = x1 + 1; x <= x2; x++)
389         {
390           int y;
391
392           for (y = y1; y <= y2; y++)
393             t->rv[x + (t->cf + 1) * y] = i_v;
394         }
395     }
396 }
397 \f
398 /* Cells. */
399
400 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
401    from V, displayed with format spec F. */
402 void
403 tab_value (struct tab_table *table, int c, int r, unsigned short opt,
404            const union value *v, const struct variable *var,
405            const struct fmt_spec *f)
406 {
407   char *contents;
408
409   if (debugging)
410     {
411       if (c + table->col_ofs < 0 || r + table->row_ofs < 0
412           || c + table->col_ofs >= tab_nc (table)
413           || r + table->row_ofs >= tab_nr (table))
414         {
415           printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
416                   "(%d,%d)\n",
417                   c, table->col_ofs, c + table->col_ofs,
418                   r, table->row_ofs, r + table->row_ofs,
419                   tab_nc (table), tab_nr (table));
420           return;
421         }
422     }
423
424   contents = data_out_stretchy (v, var_get_encoding (var),
425                                 f != NULL ? f : var_get_print_format (var),
426                                 table->container);
427
428   table->cc[c + r * table->cf] = contents;
429   table->ct[c + r * table->cf] = opt;
430 }
431
432 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
433    formatted by FMT.
434    If FMT is null, then the default print format will be used.
435 */
436 void
437 tab_double (struct tab_table *table, int c, int r, unsigned short opt,
438             double val, const struct fmt_spec *fmt, enum result_class rc)
439 {
440   union value double_value;
441   char *s;
442
443   assert (c >= 0);
444   assert (c < tab_nc (table));
445   assert (r >= 0);
446   assert (r < tab_nr (table));
447
448   if (fmt == NULL)
449     fmt = &table->fmtmap[rc];
450
451   fmt_check_output (fmt);
452
453   if (debugging)
454     {
455       if (c + table->col_ofs < 0 || r + table->row_ofs < 0
456           || c + table->col_ofs >= tab_nc (table)
457           || r + table->row_ofs >= tab_nr (table))
458         {
459           printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
460                   "(%d,%d)\n",
461                   c, table->col_ofs, c + table->col_ofs,
462                   r, table->row_ofs, r + table->row_ofs,
463                   tab_nc (table), tab_nr (table));
464           return;
465         }
466     }
467
468   double_value.f = val;
469   s = data_out_stretchy (&double_value, C_ENCODING, fmt, table->container);
470   table->cc[c + r * table->cf] = s + strspn (s, " ");
471   table->ct[c + r * table->cf] = opt;
472 }
473
474
475 static void
476 do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
477 {
478   assert (c >= 0);
479   assert (r >= 0);
480   assert (c < tab_nc (table));
481   assert (r < tab_nr (table));
482
483   if (debugging)
484     {
485       if (c + table->col_ofs < 0 || r + table->row_ofs < 0
486           || c + table->col_ofs >= tab_nc (table)
487           || r + table->row_ofs >= tab_nr (table))
488         {
489           printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
490                   "(%d,%d)\n",
491                   c, table->col_ofs, c + table->col_ofs,
492                   r, table->row_ofs, r + table->row_ofs,
493                   tab_nc (table), tab_nr (table));
494           return;
495         }
496     }
497
498   table->cc[c + r * table->cf] = text;
499   table->ct[c + r * table->cf] = opt;
500 }
501
502 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
503    TEXT. */
504 void
505 tab_text (struct tab_table *table, int c, int r, unsigned opt,
506           const char *text)
507 {
508   do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
509 }
510
511 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
512    FORMAT, which is formatted as if passed to printf. */
513 void
514 tab_text_format (struct tab_table *table, int c, int r, unsigned opt,
515                  const char *format, ...)
516 {
517   va_list args;
518
519   va_start (args, format);
520   do_tab_text (table, c, r, opt,
521                pool_vasprintf (table->container, format, args));
522   va_end (args);
523 }
524
525 static struct tab_joined_cell *
526 add_joined_cell (struct tab_table *table, int x1, int y1, int x2, int y2,
527                  unsigned opt)
528 {
529   struct tab_joined_cell *j;
530
531   assert (x1 + table->col_ofs >= 0);
532   assert (y1 + table->row_ofs >= 0);
533   assert (y2 >= y1);
534   assert (x2 >= x1);
535   assert (y2 + table->row_ofs < tab_nr (table));
536   assert (x2 + table->col_ofs < tab_nc (table));
537
538   if (debugging)
539     {
540       if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= tab_nc (table)
541           || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= tab_nr (table)
542           || x2 < x1 || x2 + table->col_ofs >= tab_nc (table)
543           || y2 < y1 || y2 + table->row_ofs >= tab_nr (table))
544         {
545           printf ("tab_joint_text(): bad cell "
546                   "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
547                   x1, table->col_ofs, x1 + table->col_ofs,
548                   y1, table->row_ofs, y1 + table->row_ofs,
549                   x2, table->col_ofs, x2 + table->col_ofs,
550                   y2, table->row_ofs, y2 + table->row_ofs,
551                   tab_nc (table), tab_nr (table));
552           return NULL;
553         }
554     }
555
556   tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
557
558   j = pool_alloc (table->container, sizeof *j);
559   j->d[TABLE_HORZ][0] = x1 + table->col_ofs;
560   j->d[TABLE_VERT][0] = y1 + table->row_ofs;
561   j->d[TABLE_HORZ][1] = ++x2 + table->col_ofs;
562   j->d[TABLE_VERT][1] = ++y2 + table->row_ofs;
563   j->n_footnotes = 0;
564   j->footnotes = NULL;
565   j->style = NULL;
566
567   {
568     void **cc = &table->cc[x1 + y1 * table->cf];
569     unsigned short *ct = &table->ct[x1 + y1 * table->cf];
570     const int ofs = table->cf - (x2 - x1);
571
572     int y;
573
574     for (y = y1; y < y2; y++)
575       {
576         int x;
577
578         for (x = x1; x < x2; x++)
579           {
580             *cc++ = j;
581             *ct++ = opt | TAB_JOIN;
582           }
583
584         cc += ofs;
585         ct += ofs;
586       }
587   }
588
589   return j;
590 }
591
592 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
593    options OPT to have text value TEXT. */
594 void
595 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
596                 unsigned opt, const char *text)
597 {
598   char *s = pool_strdup (table->container, text);
599   if (x1 == x2 && y1 == y2)
600     do_tab_text (table, x1, y1, opt, s);
601   else
602     add_joined_cell (table, x1, y1, x2, y2, opt)->u.text = s;
603 }
604
605 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them
606    with options OPT to have text value FORMAT, which is formatted
607    as if passed to printf. */
608 void
609 tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2,
610                        int y2, unsigned opt, const char *format, ...)
611 {
612   va_list args;
613   char *s;
614
615   va_start (args, format);
616   s = pool_vasprintf (table->container, format, args);
617   va_end (args);
618
619   add_joined_cell (table, x1, y1, x2, y2, opt)->u.text = s;
620 }
621
622 struct footnote *
623 tab_create_footnote (struct tab_table *table, size_t idx, const char *content,
624                      const char *marker)
625 {
626   struct footnote *f = pool_alloc (table->container, sizeof *f);
627   f->idx = idx;
628   f->content = pool_strdup (table->container, content);
629   f->marker = pool_strdup (table->container, marker);
630   return f;
631 }
632
633 void
634 tab_add_footnote (struct tab_table *table, int x, int y,
635                   const struct footnote *f)
636 {
637   int index = x + y * table->cf;
638   unsigned short opt = table->ct[index];
639   struct tab_joined_cell *j;
640
641   if (opt & TAB_JOIN)
642     j = table->cc[index];
643   else
644     {
645       char *text = table->cc[index];
646
647       j = add_joined_cell (table, x, y, x, y, table->ct[index]);
648       j->u.text = text ? text : xstrdup ("");
649     }
650
651   j->footnotes = pool_realloc (table->container, j->footnotes,
652                                (j->n_footnotes + 1) * sizeof *j->footnotes);
653
654   j->footnotes[j->n_footnotes++] = f;
655 }
656
657 void
658 tab_add_style (struct tab_table *table, int x, int y,
659                const struct cell_style *style)
660 {
661   int index = x + y * table->cf;
662   unsigned short opt = table->ct[index];
663   struct tab_joined_cell *j;
664
665   if (opt & TAB_JOIN)
666     j = table->cc[index];
667   else
668     {
669       char *text = table->cc[index];
670
671       j = add_joined_cell (table, x, y, x, y, table->ct[index]);
672       j->u.text = text ? text : xstrdup ("");
673     }
674
675   j->style = style;
676 }
677
678 bool
679 tab_cell_is_empty (const struct tab_table *table, int c, int r)
680 {
681   return table->cc[c + r * table->cf] == NULL;
682 }
683 \f
684 /* Miscellaneous. */
685
686 /* Set the title of table T to TITLE, which is formatted as if
687    passed to printf(). */
688 void
689 tab_title (struct tab_table *t, const char *title, ...)
690 {
691   va_list args;
692
693   free (t->title);
694   va_start (args, title);
695   t->title = xvasprintf (title, args);
696   va_end (args);
697 }
698
699 /* Set the caption of table T to CAPTION, which is formatted as if
700    passed to printf(). */
701 void
702 tab_caption (struct tab_table *t, const char *caption, ...)
703 {
704   va_list args;
705
706   free (t->caption);
707   va_start (args, caption);
708   t->caption = xvasprintf (caption, args);
709   va_end (args);
710 }
711
712 /* Easy, type-safe way to submit a tab table to som. */
713 void
714 tab_submit (struct tab_table *t)
715 {
716   table_item_submit (table_item_create (&t->table, t->title, t->caption));
717 }
718 \f
719 /* Editing. */
720
721 /* Set table row and column offsets for all functions that affect
722    cells or rules. */
723 void
724 tab_offset (struct tab_table *t, int col, int row)
725 {
726   int diff = 0;
727
728   if (debugging)
729     {
730       if (row < -1 || row > tab_nr (t))
731         {
732           printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
733           NOT_REACHED ();
734         }
735       if (col < -1 || col > tab_nc (t))
736         {
737           printf ("tab_offset(): col=%d in %d-column table\n", col,
738                   tab_nc (t));
739           NOT_REACHED ();
740         }
741     }
742
743   if (row != -1)
744     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
745   if (col != -1)
746     diff += (col - t->col_ofs), t->col_ofs = col;
747
748   t->cc += diff;
749   t->ct += diff;
750 }
751
752 /* Increment the row offset by one. If the table is too small,
753    increase its size. */
754 void
755 tab_next_row (struct tab_table *t)
756 {
757   t->cc += t->cf;
758   t->ct += t->cf;
759   if (++t->row_ofs >= tab_nr (t))
760     tab_realloc (t, -1, tab_nr (t) * 4 / 3);
761 }
762 \f
763 /* Writes STRING to the output.  OPTIONS may be any valid combination of TAB_*
764    bits.
765
766    This function is obsolete.  Please do not add new uses of it.  Instead, use
767    a text_item (see output/text-item.h). */
768 void
769 tab_output_text (int options, const char *string)
770 {
771   enum text_item_type type = (options & TAB_EMPH ? TEXT_ITEM_SUBHEAD
772                               : options & TAB_FIX ? TEXT_ITEM_MONOSPACE
773                               : TEXT_ITEM_PARAGRAPH);
774   text_item_submit (text_item_create (type, string));
775 }
776
777 /* Same as tab_output_text(), but FORMAT is passed through printf-like
778    formatting before output. */
779 void
780 tab_output_text_format (int options, const char *format, ...)
781 {
782   va_list args;
783   char *text;
784
785   va_start (args, format);
786   text = xvasprintf (format, args);
787   va_end (args);
788
789   tab_output_text (options, text);
790
791   free (text);
792 }
793 \f
794 /* Table class implementation. */
795
796 static void
797 tab_destroy (struct table *table)
798 {
799   struct tab_table *t = tab_cast (table);
800   free (t->title);
801   t->title = NULL;
802   free (t->caption);
803   t->caption = NULL;
804   pool_destroy (t->container);
805 }
806
807 static void
808 tab_get_cell (const struct table *table, int x, int y,
809               struct table_cell *cell)
810 {
811   const struct tab_table *t = tab_cast (table);
812   int index = x + y * t->cf;
813   unsigned short opt = t->ct[index];
814   const void *cc = t->cc[index];
815
816   cell->inline_contents.options = opt;
817   cell->inline_contents.n_footnotes = 0;
818   cell->destructor = NULL;
819
820   int style_idx = (opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT;
821   const struct cell_style *style = t->styles[style_idx];
822   if (style)
823     cell->style = style;
824
825   if (opt & TAB_JOIN)
826     {
827       const struct tab_joined_cell *jc = cc;
828       cell->contents = &cell->inline_contents;
829       cell->n_contents = 1;
830       cell->inline_contents.text = jc->u.text;
831
832       cell->inline_contents.footnotes = jc->footnotes;
833       cell->inline_contents.n_footnotes = jc->n_footnotes;
834
835       cell->d[TABLE_HORZ][0] = jc->d[TABLE_HORZ][0];
836       cell->d[TABLE_HORZ][1] = jc->d[TABLE_HORZ][1];
837       cell->d[TABLE_VERT][0] = jc->d[TABLE_VERT][0];
838       cell->d[TABLE_VERT][1] = jc->d[TABLE_VERT][1];
839
840       if (jc->style)
841         cell->style = jc->style;
842     }
843   else
844     {
845       cell->d[TABLE_HORZ][0] = x;
846       cell->d[TABLE_HORZ][1] = x + 1;
847       cell->d[TABLE_VERT][0] = y;
848       cell->d[TABLE_VERT][1] = y + 1;
849       if (cc != NULL)
850         {
851           cell->contents = &cell->inline_contents;
852           cell->n_contents = 1;
853           cell->inline_contents.text = CONST_CAST (char *, cc);
854         }
855       else
856         {
857           cell->contents = NULL;
858           cell->n_contents = 0;
859         }
860     }
861 }
862
863 static int
864 tab_get_rule (const struct table *table, enum table_axis axis, int x, int y,
865               struct cell_color *color)
866 {
867   const struct tab_table *t = tab_cast (table);
868   uint8_t raw = (axis == TABLE_VERT
869                  ? t->rh[x + t->cf * y] : t->rv[x + (t->cf + 1) * y]);
870   struct cell_color *p = t->rule_colors[(raw & TAB_RULE_STYLE_MASK)
871                                         >> TAB_RULE_STYLE_SHIFT];
872   if (p)
873     *color = *p;
874   return (raw & TAB_RULE_TYPE_MASK) >> TAB_RULE_TYPE_SHIFT;
875 }
876
877 static const struct table_class tab_table_class = {
878   tab_destroy,
879   tab_get_cell,
880   tab_get_rule,
881   NULL,                         /* paste */
882   NULL,                         /* select */
883 };
884
885 struct tab_table *
886 tab_cast (const struct table *table)
887 {
888   assert (table->klass == &tab_table_class);
889   return UP_CAST (table, struct tab_table, table);
890 }