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