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