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