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