FREQUENCIES: Fix spelling error in dialog box text.
[pspp] / 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   x1 += t->col_ofs;
240   x2 += t->col_ofs;
241   y += t->row_ofs;
242
243   assert (y >= 0);
244   assert (y <= tab_nr (t));
245   assert (x2 >= x1 );
246   assert (x1 >= 0 );
247   assert (x2 < tab_nc (t));
248
249   if (style != -1)
250     {
251       int x;
252       for (x = x1; x <= x2; x++)
253         t->rh[x + t->cf * y] = style;
254     }
255 }
256
257 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
258    lines of style F_H and vertical lines of style F_V.  Fills the
259    interior of the box with horizontal lines of style I_H and vertical
260    lines of style I_V.  Any of the line styles may be -1 to avoid
261    drawing those lines.  This is distinct from 0, which draws a null
262    line. */
263 void
264 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
265          int x1, int y1, int x2, int y2)
266 {
267 #if DEBUGGING
268   if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t)
269       || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t)
270       || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
271       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
272     {
273       printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
274                 "in table size (%d,%d)\n"),
275               x1, t->col_ofs, x1 + t->col_ofs,
276               y1, t->row_ofs, y1 + t->row_ofs,
277               x2, t->col_ofs, x2 + t->col_ofs,
278               y2, t->row_ofs, y2 + t->row_ofs,
279               tab_nc (t), tab_nr (t));
280       NOT_REACHED ();
281     }
282 #endif
283
284   x1 += t->col_ofs;
285   x2 += t->col_ofs;
286   y1 += t->row_ofs;
287   y2 += t->row_ofs;
288
289   assert (x2 >= x1);
290   assert (y2 >= y1);
291   assert (x1 >= 0);
292   assert (y1 >= 0);
293   assert (x2 < tab_nc (t));
294   assert (y2 < tab_nr (t));
295
296   if (f_h != -1)
297     {
298       int x;
299       for (x = x1; x <= x2; x++)
300         {
301           t->rh[x + t->cf * y1] = f_h;
302           t->rh[x + t->cf * (y2 + 1)] = f_h;
303         }
304     }
305   if (f_v != -1)
306     {
307       int y;
308       for (y = y1; y <= y2; y++)
309         {
310           t->rv[x1 + (t->cf + 1) * y] = f_v;
311           t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
312         }
313     }
314
315   if (i_h != -1)
316     {
317       int y;
318
319       for (y = y1 + 1; y <= y2; y++)
320         {
321           int x;
322
323           for (x = x1; x <= x2; x++)
324             t->rh[x + t->cf * y] = i_h;
325         }
326     }
327   if (i_v != -1)
328     {
329       int x;
330
331       for (x = x1 + 1; x <= x2; x++)
332         {
333           int y;
334
335           for (y = y1; y <= y2; y++)
336             t->rv[x + (t->cf + 1) * y] = i_v;
337         }
338     }
339 }
340 \f
341 /* Cells. */
342
343 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
344    from V, displayed with format spec F. */
345 void
346 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
347            const union value *v, const struct dictionary *dict, 
348            const struct fmt_spec *f)
349 {
350   char *contents;
351
352 #if DEBUGGING
353   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
354       || c + table->col_ofs >= tab_nc (table)
355       || r + table->row_ofs >= tab_nr (table))
356     {
357       printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
358               "(%d,%d)\n",
359               c, table->col_ofs, c + table->col_ofs,
360               r, table->row_ofs, r + table->row_ofs,
361               tab_nc (table), tab_nr (table));
362       return;
363     }
364 #endif
365
366   contents = data_out_pool (v, dict_get_encoding (dict), f, table->container);
367
368   table->cc[c + r * table->cf] = contents;
369   table->ct[c + r * table->cf] = opt;
370 }
371
372 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
373    with NDEC decimal places. */
374 void
375 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
376            double val, int w, int d)
377 {
378   struct fmt_spec f;
379   union value double_value;
380   char *s;
381
382   assert (c >= 0);
383   assert (c < tab_nc (table));
384   assert (r >= 0);
385   assert (r < tab_nr (table));
386
387   f = fmt_for_output (FMT_F, w, d);
388
389 #if DEBUGGING
390   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
391       || c + table->col_ofs >= tab_nc (table)
392       || r + table->row_ofs >= tab_nr (table))
393     {
394       printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
395               "(%d,%d)\n",
396               c, table->col_ofs, c + table->col_ofs,
397               r, table->row_ofs, r + table->row_ofs,
398               tab_nc (table), tab_nr (table));
399       return;
400     }
401 #endif
402
403   double_value.f = val;
404   s = data_out_pool (&double_value, LEGACY_NATIVE, &f, table->container);
405
406   table->cc[c + r * table->cf] = s + strspn (s, " ");
407   table->ct[c + r * table->cf] = opt;
408 }
409
410 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
411    formatted by FMT.
412    If FMT is null, then the default print format will be used.
413 */
414 void
415 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
416            double val, const struct fmt_spec *fmt)
417 {
418   union value double_value ;
419   char *s;
420
421   assert (c >= 0);
422   assert (c < tab_nc (table));
423   assert (r >= 0);
424   assert (r < tab_nr (table));
425
426   if ( fmt == NULL)
427     fmt = settings_get_format ();
428
429   fmt_check_output (fmt);
430
431 #if DEBUGGING
432   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
433       || c + table->col_ofs >= tab_nc (table)
434       || r + table->row_ofs >= tab_nr (table))
435     {
436       printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
437               "(%d,%d)\n",
438               c, table->col_ofs, c + table->col_ofs,
439               r, table->row_ofs, r + table->row_ofs,
440               tab_nc (table), tab_nr (table));
441       return;
442     }
443 #endif
444
445   double_value.f = val;
446   s = data_out_pool (&double_value, LEGACY_NATIVE, fmt, table->container);
447   table->cc[c + r * table->cf] = s + strspn (s, " ");
448   table->ct[c + r * table->cf] = opt;
449 }
450
451
452 static void
453 do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
454 {
455   assert (c >= 0 );
456   assert (r >= 0 );
457   assert (c < tab_nc (table));
458   assert (r < tab_nr (table));
459
460 #if DEBUGGING
461   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
462       || c + table->col_ofs >= tab_nc (table)
463       || r + table->row_ofs >= tab_nr (table))
464     {
465       printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
466               "(%d,%d)\n",
467               c, table->col_ofs, c + table->col_ofs,
468               r, table->row_ofs, r + table->row_ofs,
469               tab_nc (table), tab_nr (table));
470       return;
471     }
472 #endif
473
474   table->cc[c + r * table->cf] = text;
475   table->ct[c + r * table->cf] = opt;
476 }
477
478 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
479    TEXT. */
480 void
481 tab_text (struct tab_table *table, int c, int r, unsigned opt,
482           const char *text)
483 {
484   do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
485 }
486
487 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
488    FORMAT, which is formatted as if passed to printf. */
489 void
490 tab_text_format (struct tab_table *table, int c, int r, unsigned opt,
491                  const char *format, ...)
492 {
493   va_list args;
494
495   va_start (args, format);
496   do_tab_text (table, c, r, opt,
497                pool_vasprintf (table->container, format, args));
498   va_end (args);
499 }
500
501 static void
502 do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
503                    unsigned opt, char *text)
504 {
505   struct tab_joined_cell *j;
506
507   assert (x1 + table->col_ofs >= 0);
508   assert (y1 + table->row_ofs >= 0);
509   assert (y2 >= y1);
510   assert (x2 >= x1);
511   assert (y2 + table->row_ofs < tab_nr (table));
512   assert (x2 + table->col_ofs < tab_nc (table));
513
514 #if DEBUGGING
515   if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= tab_nc (table)
516       || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= tab_nr (table)
517       || x2 < x1 || x2 + table->col_ofs >= tab_nc (table)
518       || y2 < y2 || y2 + table->row_ofs >= tab_nr (table))
519     {
520       printf ("tab_joint_text(): bad cell "
521               "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
522               x1, table->col_ofs, x1 + table->col_ofs,
523               y1, table->row_ofs, y1 + table->row_ofs,
524               x2, table->col_ofs, x2 + table->col_ofs,
525               y2, table->row_ofs, y2 + table->row_ofs,
526               tab_nc (table), tab_nr (table));
527       return;
528     }
529 #endif
530
531   tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
532
533   j = pool_alloc (table->container, sizeof *j);
534   j->d[TABLE_HORZ][0] = x1 + table->col_ofs;
535   j->d[TABLE_VERT][0] = y1 + table->row_ofs;
536   j->d[TABLE_HORZ][1] = ++x2 + table->col_ofs;
537   j->d[TABLE_VERT][1] = ++y2 + table->row_ofs;
538   j->contents = text;
539
540   opt |= TAB_JOIN;
541
542   {
543     void **cc = &table->cc[x1 + y1 * table->cf];
544     unsigned char *ct = &table->ct[x1 + y1 * table->cf];
545     const int ofs = table->cf - (x2 - x1);
546
547     int y;
548
549     for (y = y1; y < y2; y++)
550       {
551         int x;
552
553         for (x = x1; x < x2; x++)
554           {
555             *cc++ = j;
556             *ct++ = opt;
557           }
558
559         cc += ofs;
560         ct += ofs;
561       }
562   }
563 }
564
565 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
566    options OPT to have text value TEXT. */
567 void
568 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
569                 unsigned opt, const char *text)
570 {
571   do_tab_joint_text (table, x1, y1, x2, y2, opt,
572                      pool_strdup (table->container, text));
573 }
574
575 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them
576    with options OPT to have text value FORMAT, which is formatted
577    as if passed to printf. */
578 void
579 tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, int y2,
580                        unsigned opt, const char *format, ...)
581 {
582   va_list args;
583
584   va_start (args, format);
585   do_tab_joint_text (table, x1, y1, x2, y2, opt,
586                      pool_vasprintf (table->container, format, args));
587   va_end (args);
588 }
589
590 bool
591 tab_cell_is_empty (const struct tab_table *table, int c, int r)
592 {
593   return table->cc[c + r * table->cf] == NULL;
594 }
595 \f
596 /* Miscellaneous. */
597
598 /* Set the title of table T to TITLE, which is formatted as if
599    passed to printf(). */
600 void
601 tab_title (struct tab_table *t, const char *title, ...)
602 {
603   va_list args;
604
605   free (t->title);
606   va_start (args, title);
607   t->title = xvasprintf (title, args);
608   va_end (args);
609 }
610
611 /* Easy, type-safe way to submit a tab table to som. */
612 void
613 tab_submit (struct tab_table *t)
614 {
615   table_item_submit (table_item_create (&t->table, t->title));
616 }
617 \f
618 /* Editing. */
619
620 /* Set table row and column offsets for all functions that affect
621    cells or rules. */
622 void
623 tab_offset (struct tab_table *t, int col, int row)
624 {
625   int diff = 0;
626
627 #if DEBUGGING
628   if (row < -1 || row > tab_nr (t))
629     {
630       printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
631       NOT_REACHED ();
632     }
633   if (col < -1 || col > tab_nc (t))
634     {
635       printf ("tab_offset(): col=%d in %d-column table\n", col, tab_nc (t));
636       NOT_REACHED ();
637     }
638 #endif
639
640   if (row != -1)
641     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
642   if (col != -1)
643     diff += (col - t->col_ofs), t->col_ofs = col;
644
645   t->cc += diff;
646   t->ct += diff;
647 }
648
649 /* Increment the row offset by one. If the table is too small,
650    increase its size. */
651 void
652 tab_next_row (struct tab_table *t)
653 {
654   t->cc += t->cf;
655   t->ct += t->cf;
656   if (++t->row_ofs >= tab_nr (t))
657     tab_realloc (t, -1, tab_nr (t) * 4 / 3);
658 }
659 \f
660 /* Writes STRING to the output.  OPTIONS may be any valid combination of TAB_*
661    bits.
662
663    This function is obsolete.  Please do not add new uses of it.  Instead, use
664    a text_item (see output/text-item.h). */
665 void
666 tab_output_text (int options, const char *string)
667 {
668   enum text_item_type type = (options & TAB_EMPH ? TEXT_ITEM_SUBHEAD
669                               : options & TAB_FIX ? TEXT_ITEM_MONOSPACE
670                               : TEXT_ITEM_PARAGRAPH);
671   text_item_submit (text_item_create (type, string));
672 }
673
674 /* Same as tab_output_text(), but FORMAT is passed through printf-like
675    formatting before output. */
676 void
677 tab_output_text_format (int options, const char *format, ...)
678 {
679   va_list args;
680   char *text;
681
682   va_start (args, format);
683   text = xvasprintf (format, args);
684   va_end (args);
685
686   tab_output_text (options, text);
687
688   free (text);
689 }
690 \f
691 /* Table class implementation. */
692
693 static void
694 tab_destroy (struct table *table)
695 {
696   struct tab_table *t = tab_cast (table);
697   pool_destroy (t->container);
698 }
699
700 static void
701 tab_get_cell (const struct table *table, int x, int y, struct table_cell *cell)
702 {
703   const struct tab_table *t = tab_cast (table);
704   int index = x + y * t->cf;
705   unsigned char opt = t->ct[index];
706   const void *content = t->cc[index];
707
708   cell->options = opt;
709   if (opt & TAB_JOIN)
710     {
711       const struct tab_joined_cell *jc = content;
712       cell->d[TABLE_HORZ][0] = jc->d[TABLE_HORZ][0];
713       cell->d[TABLE_HORZ][1] = jc->d[TABLE_HORZ][1];
714       cell->d[TABLE_VERT][0] = jc->d[TABLE_VERT][0];
715       cell->d[TABLE_VERT][1] = jc->d[TABLE_VERT][1];
716       cell->contents = jc->contents;
717     }
718   else
719     {
720       cell->d[TABLE_HORZ][0] = x;
721       cell->d[TABLE_HORZ][1] = x + 1;
722       cell->d[TABLE_VERT][0] = y;
723       cell->d[TABLE_VERT][1] = y + 1;
724       cell->contents = content != NULL ? content : "";
725     }
726   cell->destructor = NULL;
727 }
728
729 static int
730 tab_get_rule (const struct table *table, enum table_axis axis, int x, int y)
731 {
732   const struct tab_table *t = tab_cast (table);
733   return (axis == TABLE_VERT
734           ? t->rh[x + t->cf * y]
735           : t->rv[x + (t->cf + 1) * y]);
736 }
737
738 static const struct table_class tab_table_class =
739   {
740     tab_destroy,
741     tab_get_cell,
742     tab_get_rule,
743     NULL,                       /* paste */
744     NULL,                       /* select */
745   };
746
747 struct tab_table *
748 tab_cast (const struct table *table)
749 {
750   assert (table->class == &tab_table_class);
751   return UP_CAST (table, struct tab_table, table);
752 }