862028a8c5ab56959896e8b9cb8c9deb1f9c1c65
[pspp-builds.git] / src / tab.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include "tab.h"
22 #include <ctype.h>
23 #include <assert.h>
24 #include <stdarg.h>
25 #include <limits.h>
26 #include <stdlib.h>
27 #include "alloc.h"
28 #include "command.h"
29 #include "format.h"
30 #include "magic.h"
31 #include "misc.h"
32 #include "output.h"
33 #include "pool.h"
34 #include "som.h"
35 #include "var.h"
36
37 #include "debug-print.h"
38 \f
39 struct som_table_class tab_table_class;
40
41 #if DEBUGGING
42 #define DEFFIRST(NAME, LABEL) LABEL,
43 #define DEFTAB(NAME, LABEL) LABEL,
44 /*
45 static const char *tab_names[] =
46   {
47 #include "tab.def"
48   };
49 */
50 #undef DEFFIRST
51 #undef DEFTAB
52 #endif
53
54 /* Creates a table with NC columns and NR rows.  If REALLOCABLE is
55    nonzero then the table's size can be increased later; otherwise,
56    its size can only be reduced. */
57 struct tab_table *
58 tab_create (int nc, int nr, int reallocable)
59 {
60   void *(*alloc_func) (struct pool *, size_t);
61
62   struct tab_table *t;
63   
64   {
65     struct pool *container = pool_create ();
66     t = pool_alloc (container, sizeof *t);
67     t->container = container;
68   }
69   
70   t->col_style = TAB_COL_NONE;
71   t->col_group = 0;
72   ls_null (&t->title);
73   t->flags = SOMF_NONE;
74   t->nr = nr;
75   t->nc = t->cf = nc;
76   t->l = t->r = t->t = t->b = 0;
77
78   alloc_func = reallocable ? pool_malloc : pool_alloc;
79 #if GLOBAL_DEBUGGING
80   t->reallocable = reallocable;
81 #endif
82
83   t->cc = alloc_func (t->container, nr * nc * sizeof *t->cc);
84   t->ct = alloc_func (t->container, nr * nc);
85   memset (t->ct, TAB_EMPTY, nc * nr);
86
87   t->rh = alloc_func (t->container, nc * (nr + 1));
88   memset (t->rh, 0, nc * (nr + 1));
89
90   t->hrh = alloc_func (t->container, sizeof *t->hrh * (nr + 1));
91   memset (t->hrh, 0, sizeof *t->hrh * (nr + 1));
92
93   t->trh = alloc_func (t->container, nr + 1);
94   memset (t->trh, 0, nr + 1);
95
96   t->rv = alloc_func (t->container, (nc + 1) * nr);
97   memset (t->rv, 0, (nc + 1) * nr);
98
99   t->wrv = alloc_func (t->container, sizeof *t->wrv * (nc + 1));
100   memset (t->wrv, 0, sizeof *t->wrv * (nc + 1));
101
102   t->trv = alloc_func (t->container, nc + 1);
103   memset (t->trv, 0, nc + 1);
104
105   t->dim = NULL;
106   t->w = t->h = NULL;
107   t->col_ofs = t->row_ofs = 0;
108   
109   return t;
110 }
111
112 /* Destroys table T. */
113 void
114 tab_destroy (struct tab_table *t)
115 {
116   assert (t != NULL);
117   pool_destroy (t->container);
118   t=0;
119 }
120
121 /* Sets the width and height of a table, in columns and rows,
122    respectively.  Use only to reduce the size of a table, since it
123    does not change the amount of allocated memory. */
124 void
125 tab_resize (struct tab_table *t, int nc, int nr)
126 {
127   assert (t != NULL);
128   if (nc != -1)
129     {
130       assert (nc + t->col_ofs <= t->cf);
131       t->nc = nc + t->col_ofs;
132     }
133   if (nr != -1)
134     {
135       assert (nr + t->row_ofs <= t->nr);
136       t->nr = nr + t->row_ofs;
137     }
138 }
139
140 /* Changes either or both dimensions of a table.  Consider using the
141    above routine instead if it won't waste a lot of space.
142
143    Changing the number of columns in a table is particularly expensive
144    in space and time.  Avoid doing such.  FIXME: In fact, transferring
145    of rules isn't even implemented yet. */
146 void
147 tab_realloc (struct tab_table *t, int nc, int nr)
148 {
149   int ro, co;
150   
151   assert (t != NULL);
152 #if GLOBAL_DEBUGGING
153   assert (t->reallocable);
154 #endif
155   ro = t->row_ofs;
156   co = t->col_ofs;
157   if (ro || co)
158     tab_offset (t, 0, 0);
159
160   if (nc == -1)
161     nc = t->nc;
162   if (nr == -1)
163     nr = t->nr;
164   
165   assert (nc == t->nc);
166   
167   if (nc > t->cf)
168     {
169       int mr1 = min (nr, t->nr);
170       int mc1 = min (nc, t->nc);
171       
172       struct len_string *new_cc;
173       unsigned char *new_ct;
174       int r;
175
176       new_cc = pool_malloc (t->container, nr * nc * sizeof *new_cc);
177       new_ct = pool_malloc (t->container, nr * nc);
178       for (r = 0; r < mr1; r++)
179         {
180           memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
181           memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
182           memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
183         }
184       pool_free (t->container, t->cc);
185       pool_free (t->container, t->ct);
186       t->cc = new_cc;
187       t->ct = new_ct;
188       t->cf = nc;
189     }
190   else if (nr != t->nr)
191     {
192       t->cc = pool_realloc (t->container, t->cc, nr * nc * sizeof *t->cc);
193       t->ct = pool_realloc (t->container, t->ct, nr * nc);
194
195       t->rh = pool_realloc (t->container, t->rh, nc * (nr + 1));
196       t->rv = pool_realloc (t->container, t->rv, (nc + 1) * nr);
197       t->trh = pool_realloc (t->container, t->trh, nr + 1);
198       t->hrh = pool_realloc (t->container, t->hrh,
199                              sizeof *t->hrh * (nr + 1));
200       
201       if (nr > t->nr)
202         {
203           memset (&t->rh[nc * (t->nr + 1)], 0, (nr - t->nr) * nc);
204           memset (&t->rv[(nc + 1) * t->nr], 0, (nr - t->nr) * (nc + 1));
205           memset (&t->trh[t->nr + 1], 0, nr - t->nr);
206         }
207     }
208
209   memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
210   
211   t->nr = nr;
212   t->nc = nc;
213
214   if (ro || co)
215     tab_offset (t, co, ro);
216 }
217
218 /* Sets the number of header rows on each side of TABLE to L on the
219    left, R on the right, T on the top, B on the bottom.  Header rows
220    are repeated when a table is broken across multiple columns or
221    multiple pages. */
222 void
223 tab_headers (struct tab_table *table, int l, int r, int t, int b)
224 {
225   assert (table != NULL);
226   table->l = l;
227   table->r = r;
228   table->t = t;
229   table->b = b;
230 }
231
232 /* Set up table T so that, when it is an appropriate size, it will be
233    displayed across the page in columns.
234
235    STYLE is a TAB_COL_* constant.  GROUP is the number of rows to take
236    as a unit. */
237 void
238 tab_columns (struct tab_table *t, int style, int group)
239 {
240   assert (t != NULL);
241   t->col_style = style;
242   t->col_group = group;
243 }
244 \f
245 /* Rules. */
246
247 /* Draws a vertical line to the left of cells at horizontal position X
248    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
249 void
250 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
251 {
252   int y;
253
254   assert (t != NULL);
255   assert (x  > 0);
256   assert (x  < t->nc);
257   assert (y1 >= 0);
258   assert (y2 >= y1);
259   assert (y2 <=  t->nr);
260
261 #if GLOBAL_DEBUGGING
262   if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
263       || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
264       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
265     {
266       printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
267                 "table size (%d,%d)\n"),
268               x, t->col_ofs, x + t->col_ofs,
269               y1, t->row_ofs, y1 + t->row_ofs,
270               y2, t->row_ofs, y2 + t->row_ofs,
271               t->nc, t->nr);
272       return;
273     }
274 #endif
275
276   x += t->col_ofs;
277   y1 += t->row_ofs;
278   y2 += t->row_ofs;
279
280   if (style != -1)
281     {
282       if ((style & TAL_SPACING) == 0)
283         for (y = y1; y <= y2; y++)
284           t->rv[x + (t->cf + 1) * y] = style;
285       t->trv[x] |= (1 << (style & ~TAL_SPACING));
286     }
287 }
288
289 /* Draws a horizontal line above cells at vertical position Y from X1
290    to X2 inclusive in style STYLE, if style is not -1. */
291 void
292 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
293 {
294   int x;
295
296   assert (t != NULL);
297
298   assert (y >= 0);
299   assert (y < t->nr);
300   assert (x2 >= x1 );
301   assert (x1 >= 0 );
302   assert (x2 < t->nc);
303
304   x1 += t->col_ofs;
305   x2 += t->col_ofs;
306   y += t->row_ofs;
307
308   if (style != -1)
309     {
310       if ((style & TAL_SPACING) == 0)
311         for (x = x1; x <= x2; x++)
312           t->rh[x + t->cf * y] = style;
313       t->trh[y] |= (1 << (style & ~TAL_SPACING));
314     }
315 }
316
317 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
318    lines of style F_H and vertical lines of style F_V.  Fills the
319    interior of the box with horizontal lines of style I_H and vertical
320    lines of style I_V.  Any of the line styles may be -1 to avoid
321    drawing those lines.  This is distinct from 0, which draws a null
322    line. */
323 void
324 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
325          int x1, int y1, int x2, int y2)
326 {
327   assert (t != NULL);
328
329   assert (x2 >= x1);
330   assert (y2 >= y1);
331   assert (x1 >= 0);
332   assert (y1 >= 0);
333   assert (x2 < t->nc);
334   assert (y2 < t->nr);
335
336 #if GLOBAL_DEBUGGING
337   if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc 
338       || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
339       || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr 
340       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
341     {
342       printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
343                 "in table size (%d,%d)\n"),
344               x1, t->col_ofs, x1 + t->col_ofs,
345               y1, t->row_ofs, y1 + t->row_ofs,
346               x2, t->col_ofs, x2 + t->col_ofs,
347               y2, t->row_ofs, y2 + t->row_ofs,
348               t->nc, t->nr);
349       abort ();
350     }
351 #endif
352
353   x1 += t->col_ofs;
354   x2 += t->col_ofs;
355   y1 += t->row_ofs;
356   y2 += t->row_ofs;
357
358   if (f_h != -1)
359     {
360       int x;
361       if ((f_h & TAL_SPACING) == 0)
362         for (x = x1; x <= x2; x++)
363           {
364             t->rh[x + t->cf * y1] = f_h;
365             t->rh[x + t->cf * (y2 + 1)] = f_h;
366           }
367       t->trh[y1] |= (1 << (f_h & ~TAL_SPACING));
368       t->trh[y2 + 1] |= (1 << (f_h & ~TAL_SPACING));
369     }
370   if (f_v != -1)
371     {
372       int y;
373       if ((f_v & TAL_SPACING) == 0)
374         for (y = y1; y <= y2; y++)
375           {
376             t->rv[x1 + (t->cf + 1) * y] = f_v;
377             t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
378           }
379       t->trv[x1] |= (1 << (f_v & ~TAL_SPACING));
380       t->trv[x2 + 1] |= (1 << (f_v & ~TAL_SPACING));
381     }
382
383   if (i_h != -1)
384     {
385       int y;
386       
387       for (y = y1 + 1; y <= y2; y++)
388         {
389           int x;
390
391           if ((i_h & TAL_SPACING) == 0)
392             for (x = x1; x <= x2; x++)
393               t->rh[x + t->cf * y] = i_h;
394
395           t->trh[y] |= (1 << (i_h & ~TAL_SPACING));
396         }
397     }
398   if (i_v != -1)
399     {
400       int x;
401       
402       for (x = x1 + 1; x <= x2; x++)
403         {
404           int y;
405           
406           if ((i_v & TAL_SPACING) == 0)
407             for (y = y1; y <= y2; y++)
408               t->rv[x + (t->cf + 1) * y] = i_v;
409
410           t->trv[x] |= (1 << (i_v & ~TAL_SPACING));
411         }
412     }
413 }
414
415 /* Formats text TEXT and arguments ARGS as indicated in OPT and sets
416    the resultant string into S in TABLE's pool. */
417 static void
418 text_format (struct tab_table *table, int opt, const char *text, va_list args,
419              struct len_string *s)
420 {
421   int len;
422   
423   assert (table != NULL && text != NULL && s != NULL);
424   
425   if (opt & TAT_PRINTF)
426     {
427       char *temp_buf = local_alloc (1024);
428       
429       len = nvsprintf (temp_buf, text, args);
430       text = temp_buf;
431     }
432   else
433     len = strlen (text);
434
435   ls_create_buffer (table->container, s, text, len);
436   
437   if (opt & TAT_PRINTF)
438     local_free (text);
439 }
440
441 /* Set the title of table T to TITLE, which is formatted with printf
442    if FORMAT is nonzero. */
443 void
444 tab_title (struct tab_table *t, int format, const char *title, ...)
445 {
446   va_list args;
447
448   assert (t != NULL && title != NULL);
449   va_start (args, title);
450   text_format (t, format ? TAT_PRINTF : TAT_NONE, title, args, &t->title);
451   va_end (args);
452 }
453
454 /* Set DIM_FUNC as the dimension function for table T. */
455 void
456 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
457 {
458   assert (t != NULL && t->dim == NULL);
459   t->dim = dim_func;
460 }
461
462 /* Returns the natural width of column C in table T for driver D, that
463    is, the smallest width necessary to display all its cells without
464    wrapping.  The width will be no larger than the page width minus
465    left and right rule widths. */
466 int
467 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
468 {
469   int width;
470
471   assert (t != NULL && c >= 0 && c < t->nc);
472   {
473     int r;
474
475     for (width = r = 0; r < t->nr; r++)
476       {
477         struct outp_text text;
478         unsigned char opt = t->ct[c + r * t->cf];
479                 
480         if (opt & (TAB_JOIN | TAB_EMPTY))
481           continue;
482
483         text.s = t->cc[c + r * t->cf];
484         assert (!ls_null_p (&text.s));
485         text.options = OUTP_T_JUST_LEFT;
486
487         d->class->text_metrics (d, &text);
488         if (text.h > width)
489           width = text.h;
490       }
491   }
492
493   if (width == 0)
494     {
495       width = d->prop_em_width * 8;
496 #if GLOBAL_DEBUGGING
497       printf ("warning: table column %d contains no data.\n", c);
498 #endif
499     }
500   
501   {
502     const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
503     
504     if (width > clamp)
505       width = clamp;
506   }
507
508   return width;
509 }
510
511 /* Returns the natural height of row R in table T for driver D, that
512    is, the minimum height necessary to display the information in the
513    cell at the widths set for each column. */
514 int
515 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
516 {
517   int height;
518
519   assert (t != NULL && r >= 0 && r < t->nr);
520   
521   {
522     int c;
523     
524     for (height = d->font_height, c = 0; c < t->nc; c++)
525       {
526         struct outp_text text;
527         unsigned char opt = t->ct[c + r * t->cf];
528
529         assert (t->w[c] != NOT_INT);
530         if (opt & (TAB_JOIN | TAB_EMPTY))
531           continue;
532
533         text.s = t->cc[c + r * t->cf];
534         assert (!ls_null_p (&text.s));
535         text.options = OUTP_T_HORZ | OUTP_T_JUST_LEFT;
536         text.h = t->w[c];
537         d->class->text_metrics (d, &text);
538
539         if (text.v > height)
540           height = text.v;
541       }
542   }
543
544   return height;
545 }
546
547 /* Callback function to set all columns and rows to their natural
548    dimensions.  Not really meant to be called directly.  */
549 void
550 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
551 {
552   int i;
553
554   assert (t != NULL);
555   
556   for (i = 0; i < t->nc; i++)
557     t->w[i] = tab_natural_width (t, d, i);
558   
559   for (i = 0; i < t->nr; i++)
560     t->h[i] = tab_natural_height (t, d, i);
561 }
562
563 \f
564 /* Cells. */
565
566 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
567    from V, displayed with format spec F. */
568 void
569 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
570            const union value *v, const struct fmt_spec *f)
571 {
572   char *contents;
573   union value temp_val;
574
575   assert (table != NULL && v != NULL && f != NULL);
576 #if GLOBAL_DEBUGGING
577   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
578       || c + table->col_ofs >= table->nc
579       || r + table->row_ofs >= table->nr)
580     {
581       printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
582               "(%d,%d)\n",
583               c, table->col_ofs, c + table->col_ofs,
584               r, table->row_ofs, r + table->row_ofs,
585               table->nc, table->nr);
586       return;
587     }
588 #endif
589
590   contents = pool_alloc (table->container, f->w);
591   ls_init (&table->cc[c + r * table->cf], contents, f->w);
592   table->ct[c + r * table->cf] = opt;
593   
594   if (formats[f->type].cat & FCAT_STRING)
595     {
596       temp_val.c = (char *) v->s;
597       v = &temp_val;
598     }
599   data_out (contents, f, v);
600 }
601
602 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
603    with NDEC decimal places. */
604 void
605 tab_float (struct tab_table *table, int c, int r, unsigned char opt,
606            double val, int w, int d)
607 {
608   char *contents;
609   char buf[40], *cp;
610   
611   struct fmt_spec f;
612
613   assert (table != NULL && w <= 40);
614   
615   assert (c >= 0);
616   assert (c < table->nc);
617   assert (r >= 0);
618   assert (r < table->nr);
619
620   f.type = FMT_F;
621   f.w = w;
622   f.d = d;
623   
624 #if GLOBAL_DEBUGGING
625   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
626       || c + table->col_ofs >= table->nc
627       || r + table->row_ofs >= table->nr)
628     {
629       printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
630               "(%d,%d)\n",
631               c, table->col_ofs, c + table->col_ofs,
632               r, table->row_ofs, r + table->row_ofs,
633               table->nc, table->nr);
634       return;
635     }
636 #endif
637
638   data_out (buf, &f, (union value *) &val);
639   cp = buf;
640   while (isspace ((unsigned char) *cp) && cp < &buf[w])
641     cp++;
642   f.w = w - (cp - buf);
643
644   contents = pool_alloc (table->container, f.w);
645   ls_init (&table->cc[c + r * table->cf], contents, f.w);
646   table->ct[c + r * table->cf] = opt;
647   memcpy (contents, cp, f.w);
648 }
649
650 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
651    TEXT. */
652 void
653 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
654 {
655   va_list args;
656
657   assert (table != NULL && text != NULL);
658
659   assert (c >= 0 );
660   assert (r >= 0 );
661   assert (c < table->nc);
662   assert (r < table->nr);
663   
664
665 #if GLOBAL_DEBUGGING
666   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
667       || c + table->col_ofs >= table->nc
668       || r + table->row_ofs >= table->nr)
669     {
670       printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
671               "(%d,%d)\n",
672               c, table->col_ofs, c + table->col_ofs,
673               r, table->row_ofs, r + table->row_ofs,
674               table->nc, table->nr);
675       return;
676     }
677 #endif
678     
679   va_start (args, text);
680   text_format (table, opt, text, args, &table->cc[c + r * table->cf]);
681   table->ct[c + r * table->cf] = opt;
682   va_end (args);
683 }
684
685 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
686    options OPT to have text value TEXT. */
687 void
688 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
689                 unsigned opt, const char *text, ...)
690 {
691   struct tab_joined_cell *j;
692
693   assert (table != NULL && text != NULL);
694
695   assert (x1 >= 0);
696   assert (y1 >= 0);
697   assert (y2 >= y1);
698   assert (x2 >= x1);
699   assert (y2 < table->nr);
700   assert (x2 < table->nc);
701
702 #if GLOBAL_DEBUGGING
703   if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
704       || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
705       || x2 < x1 || x2 + table->col_ofs >= table->nc
706       || y2 < y2 || y2 + table->row_ofs >= table->nr)
707     {
708       printf ("tab_joint_text(): bad cell "
709               "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
710               x1, table->col_ofs, x1 + table->col_ofs,
711               y1, table->row_ofs, y1 + table->row_ofs,
712               x2, table->col_ofs, x2 + table->col_ofs,
713               y2, table->row_ofs, y2 + table->row_ofs,
714               table->nc, table->nr);
715       return;
716     }
717 #endif
718   
719   j = pool_alloc (table->container, sizeof *j);
720   j->hit = 0;
721   j->x1 = x1 + table->col_ofs;
722   j->y1 = y1 + table->row_ofs;
723   j->x2 = ++x2 + table->col_ofs;
724   j->y2 = ++y2 + table->row_ofs;
725   
726   {
727     va_list args;
728     
729     va_start (args, text);
730     text_format (table, opt, text, args, &j->contents);
731     va_end (args);
732   }
733   
734   opt |= TAB_JOIN;
735   
736   {
737     struct len_string *cc = &table->cc[x1 + y1 * table->cf];
738     unsigned char *ct = &table->ct[x1 + y1 * table->cf];
739     const int ofs = table->cf - (x2 - x1);
740
741     int y;
742     
743     for (y = y1; y < y2; y++)
744       {
745         int x;
746         
747         for (x = x1; x < x2; x++)
748           {
749             ls_init (cc++, (char *) j, 0);
750             *ct++ = opt;
751           }
752         
753         cc += ofs;
754         ct += ofs;
755       }
756   }
757 }
758
759 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
760 void
761 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
762          struct len_string *string)
763 {
764   assert (table != NULL && string != NULL);
765   
766 #if GLOBAL_DEBUGGING
767   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
768       || c + table->col_ofs >= table->nc
769       || r + table->row_ofs >= table->nr)
770     {
771       printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
772               "(%d,%d)\n",
773               c, table->col_ofs, c + table->col_ofs,
774               r, table->row_ofs, r + table->row_ofs,
775               table->nc, table->nr);
776       return;
777     }
778 #endif
779
780   table->cc[c + r * table->cf] = *string;
781   table->ct[c + r * table->cf] = opt;
782 }
783 \f
784 /* Miscellaneous. */
785
786 /* Sets the widths of all the columns and heights of all the rows in
787    table T for driver D. */
788 static void
789 nowrap_dim (struct tab_table *t, struct outp_driver *d)
790 {
791   t->w[0] = tab_natural_width (t, d, 0);
792   t->h[0] = d->font_height;
793 }
794
795 /* Sets the widths of all the columns and heights of all the rows in
796    table T for driver D. */
797 static void
798 wrap_dim (struct tab_table *t, struct outp_driver *d)
799 {
800   t->w[0] = tab_natural_width (t, d, 0);
801   t->h[0] = tab_natural_height (t, d, 0);
802 }
803
804 /* Outputs text BUF as a table with a single cell having cell options
805    OPTIONS, which is a combination of the TAB_* and TAT_*
806    constants. */
807 void
808 tab_output_text (int options, const char *buf, ...)
809 {
810   struct tab_table *t = tab_create (1, 1, 0);
811
812   assert (buf != NULL);
813   if (options & TAT_PRINTF)
814     {
815       va_list args;
816       char *temp_buf = local_alloc (4096);
817       
818       va_start (args, buf);
819       nvsprintf (temp_buf, buf, args);
820       buf = temp_buf;
821       va_end (args);
822     }
823   
824   if (options & TAT_FIX)
825     {
826       struct outp_driver *d;
827
828       for (d = outp_drivers (NULL); d; d = outp_drivers (d))
829         {
830           if (!d->page_open)
831             d->class->open_page (d);
832
833           if (d->class->text_set_font_by_name != NULL)
834             d->class->text_set_font_by_name (d, "FIXED");
835           else 
836             {
837               /* FIXME */
838             }
839         }
840     }
841
842   tab_text (t, 0, 0, options &~ TAT_PRINTF, buf);
843   tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
844   if (options & TAT_NOWRAP)
845     tab_dim (t, nowrap_dim);
846   else
847     tab_dim (t, wrap_dim);
848   tab_submit (t);
849
850   if (options & TAT_FIX)
851     {
852       struct outp_driver *d;
853
854       for (d = outp_drivers (NULL); d; d = outp_drivers (d))
855         if (d->class->text_set_font_by_name != NULL)
856           d->class->text_set_font_by_name (d, "PROP");
857         else 
858           {
859             /* FIXME */
860           }
861     }
862   
863   if (options & TAT_PRINTF)
864     local_free (buf);
865 }
866
867 /* Set table flags to FLAGS. */
868 void
869 tab_flags (struct tab_table *t, unsigned flags)
870 {
871   assert (t != NULL);
872   t->flags = flags;
873 }
874
875 /* Easy, type-safe way to submit a tab table to som. */
876 void
877 tab_submit (struct tab_table *t)
878 {
879   struct som_table s;
880
881   assert (t != NULL);
882   s.class = &tab_table_class;
883   s.ext = t;
884   som_submit (&s);
885   tab_destroy (t);
886 }
887 \f
888 /* Editing. */
889
890 /* Set table row and column offsets for all functions that affect
891    cells or rules. */
892 void
893 tab_offset (struct tab_table *t, int col, int row)
894 {
895   int diff = 0;
896
897   assert (t != NULL);
898 #if GLOBAL_DEBUGGING
899   if (row < -1 || row >= t->nr)
900     {
901       printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
902       abort ();
903     }
904   if (col < -1 || col >= t->nc)
905     {
906       printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
907       abort ();
908     }
909 #endif
910
911   if (row != -1)
912     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
913   if (col != -1)
914     diff += (col - t->col_ofs), t->col_ofs = col;
915
916   t->cc += diff;
917   t->ct += diff;
918 }
919
920 /* Increment the row offset by one. If the table is too small,
921    increase its size. */
922 void
923 tab_next_row (struct tab_table *t)
924 {
925   assert (t != NULL);
926   t->cc += t->cf;
927   t->ct += t->cf;
928   if (++t->row_ofs >= t->nr)
929     tab_realloc (t, -1, t->nr * 4 / 3);
930 }
931 \f
932 static struct tab_table *t;
933 static struct outp_driver *d;
934 int tab_hit;
935
936 /* Set the current table to TABLE. */
937 static void
938 tabi_table (struct som_table *table)
939 {
940   assert (table != NULL);
941   t = table->ext;
942   tab_offset (t, 0, 0);
943   
944   assert (t->w == NULL && t->h == NULL);
945   t->w = pool_alloc (t->container, sizeof *t->w * t->nc);
946   t->h = pool_alloc (t->container, sizeof *t->h * t->nr);
947 }
948
949 /* Set the current output device to DRIVER. */
950 static void
951 tabi_driver (struct outp_driver *driver)
952 {
953   int i;
954
955   assert (driver != NULL);
956   d = driver;
957   
958   /* Figure out sizes of rules. */
959   for (t->hr_tot = i = 0; i <= t->nr; i++)
960     t->hr_tot += t->hrh[i] = d->horiz_line_spacing[t->trh[i]];
961   for (t->vr_tot = i = 0; i <= t->nc; i++)
962     t->vr_tot += t->wrv[i] = d->vert_line_spacing[t->trv[i]];
963
964 #if GLOBAL_DEBUGGING
965   for (i = 0; i < t->nr; i++)
966     t->h[i] = -1;
967   for (i = 0; i < t->nc; i++)
968     t->w[i] = -1;
969 #endif
970
971   assert (t->dim != NULL);
972   t->dim (t, d);
973
974 #if GLOBAL_DEBUGGING
975   {
976     int error = 0;
977
978     for (i = 0; i < t->nr; i++)
979       {
980         if (t->h[i] == -1)
981           {
982             printf ("Table row %d height not initialized.\n", i);
983             error = 1;
984           }
985         assert (t->h[i] > 0);
986       }
987     
988     for (i = 0; i < t->nc; i++)
989       {
990         if (t->w[i] == -1)
991           {
992             printf ("Table column %d width not initialized.\n", i);
993             error = 1;
994           }
995         assert (t->w[i] > 0);
996       }
997   }
998 #endif
999     
1000   /* Add up header sizes. */
1001   for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
1002     t->wl += t->w[i] + t->wrv[i + 1];
1003   for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
1004     t->ht += t->h[i] + t->hrh[i + 1];
1005   for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
1006     t->wr += t->w[i] + t->wrv[i + 1];
1007   for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
1008     t->hb += t->h[i] + t->hrh[i + 1];
1009   
1010   /* Title. */
1011   if (!(t->flags & SOMF_NO_TITLE))
1012     t->ht += d->font_height;
1013 }
1014
1015 /* Return the number of columns and rows in the table into N_COLUMNS
1016    and N_ROWS, respectively. */
1017 static void
1018 tabi_count (int *n_columns, int *n_rows)
1019 {
1020   assert (n_columns != NULL && n_rows != NULL);
1021   *n_columns = t->nc;
1022   *n_rows = t->nr;
1023 }
1024
1025 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
1026
1027 /* Return the horizontal and vertical size of the entire table,
1028    including headers, for the current output device, into HORIZ and
1029    VERT. */
1030 static void
1031 tabi_area (int *horiz, int *vert)
1032 {
1033   assert (horiz != NULL && vert != NULL);
1034   
1035   {
1036     int w, c;
1037     
1038     for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
1039          c < t->nc - t->r; c++)
1040       w += t->w[c] + t->wrv[c];
1041     *horiz = w;
1042   }
1043   
1044   {
1045     int h, r;
1046     for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1047          r < t->nr - t->b; r++)
1048       h += t->h[r] + t->hrh[r];
1049     *vert = h;
1050   }
1051 }
1052
1053 /* Return the column style for this table into STYLE. */
1054 static void
1055 tabi_columns (int *style)
1056 {
1057   assert (style != NULL);
1058   *style = t->col_style;
1059 }
1060
1061 /* Return the number of header rows/columns on the left, right, top,
1062    and bottom sides into HL, HR, HT, and HB, respectively. */
1063 static void
1064 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1065 {
1066   assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1067   *hl = t->l;
1068   *hr = t->r;
1069   *ht = t->t;
1070   *hb = t->b;
1071 }
1072
1073 /* Determines the number of rows or columns (including appropriate
1074    headers), depending on CUMTYPE, that will fit into the space
1075    specified.  Takes rows/columns starting at index START and attempts
1076    to fill up available space MAX.  Returns in END the index of the
1077    last row/column plus one; returns in ACTUAL the actual amount of
1078    space the selected rows/columns (including appropriate headers)
1079    filled. */
1080 static void
1081 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1082 {
1083   int n;
1084   int *d;
1085   int *r;
1086   int total;
1087   
1088   assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1089   if (cumtype == SOM_ROWS)
1090     {
1091       assert (start >= 0 && start < t->nr);
1092       n = t->nr - t->b;
1093       d = &t->h[start];
1094       r = &t->hrh[start + 1];
1095       total = t->ht + t->hb;
1096     } else {
1097       assert (start >= 0 && start < t->nc);
1098       n = t->nc - t->r;
1099       d = &t->w[start];
1100       r = &t->wrv[start + 1];
1101       total = t->wl + t->wr;
1102     }
1103   
1104   total += *d++;
1105   if (total > max)
1106     {
1107       if (end)
1108         *end = start;
1109       if (actual)
1110         *actual = 0;
1111       return;
1112     }
1113     
1114   {
1115     int x;
1116       
1117     for (x = start + 1; x < n; x++)
1118       {
1119         int amt = *d++ + *r++;
1120         
1121         total += amt;
1122         if (total > max)
1123           {
1124             total -= amt;
1125             break;
1126           }
1127       }
1128
1129     if (end)
1130       *end = x;
1131     
1132     if (actual)
1133       *actual = total;
1134   }
1135 }
1136
1137 /* Return flags set for the current table into FLAGS. */
1138 static void
1139 tabi_flags (unsigned *flags)
1140 {
1141   assert (flags != NULL);
1142   *flags = t->flags;
1143 }
1144
1145 /* Render title for current table, with major index X and minor index
1146    Y.  Y may be zero, or X and Y may be zero, but X should be nonzero
1147    if Y is nonzero. */
1148 static void
1149 tabi_title (int x, int y)
1150 {
1151   char buf[1024];
1152   char *cp;
1153
1154   if (t->flags & SOMF_NO_TITLE)
1155     return;
1156   
1157   cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1158   if (x && y)
1159     cp = spprintf (cp, "(%d:%d)", x, y);
1160   else if (x)
1161     cp = spprintf (cp, "(%d)", x);
1162   if (cur_proc)
1163     cp = spprintf (cp, " %s", cur_proc);
1164   cp = stpcpy (cp, ".  ");
1165   if (!ls_empty_p (&t->title))
1166     {
1167       memcpy (cp, ls_value (&t->title), ls_length (&t->title));
1168       cp += ls_length (&t->title);
1169     }
1170   *cp = 0;
1171   
1172   {
1173     struct outp_text text;
1174
1175     text.options = OUTP_T_JUST_LEFT | OUTP_T_HORZ | OUTP_T_VERT;
1176     ls_init (&text.s, buf, cp - buf);
1177     text.h = d->width;
1178     text.v = d->font_height;
1179     text.x = 0;
1180     text.y = d->cp_y;
1181     d->class->text_draw (d, &text);
1182   }
1183 }
1184
1185 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1186
1187 /* Draws the table region in rectangle (X1,Y1)-(X2,Y2), where column
1188    X2 and row Y2 are not included in the rectangle, at the current
1189    position on the current output device.  Draws headers as well. */
1190 static void
1191 tabi_render (int x1, int y1, int x2, int y2)
1192 {
1193   int i, y;
1194   int ranges[3][2];
1195   
1196   tab_hit++;
1197
1198   y = d->cp_y;
1199   if (!(t->flags & SOMF_NO_TITLE))
1200     y += d->font_height;
1201
1202   /* Top headers. */
1203   ranges[0][0] = 0;
1204   ranges[0][1] = t->t * 2 + 1;
1205
1206   /* Requested rows. */
1207   ranges[1][0] = y1 * 2 + 1;
1208   ranges[1][1] = y2 * 2;
1209
1210   /* Bottom headers. */
1211   ranges[2][0] = (t->nr - t->b) * 2;
1212   ranges[2][1] = t->nr * 2 + 1;
1213
1214   for (i = 0; i < 3; i++) 
1215     {
1216       int r;
1217
1218       for (r = ranges[i][0]; r < ranges[i][1]; r++) 
1219         {
1220           int x = d->cp_x;
1221           x += render_strip (x, y, r, 0, t->l * 2 + 1, y1, y2);
1222           x += render_strip (x, y, r, x1 * 2 + 1, x2 * 2, y1, y2);
1223           x += render_strip (x, y, r, (t->nc - t->r) * 2,
1224                              t->nc * 2 + 1, y1, y2);
1225           y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2]; 
1226         }
1227     }
1228 }
1229
1230 struct som_table_class tab_table_class =
1231   {
1232     tabi_table,
1233     tabi_driver,
1234     
1235     tabi_count,
1236     tabi_area,
1237     NULL,
1238     NULL,
1239     tabi_columns,
1240     NULL,
1241     tabi_headers,
1242     NULL,
1243     tabi_cumulate,
1244     tabi_flags,
1245     
1246     NULL,
1247     NULL,
1248
1249     tabi_title,
1250     tabi_render,
1251   };
1252 \f
1253 /* Render contiguous strip consisting of columns C1...C2, exclusive,
1254    on row R, at location (X,Y).  Return width of the strip thus
1255    rendered.
1256
1257    Renders joined cells, even those outside the strip, within the
1258    rendering region (C1,R1)-(C2,R2).
1259
1260    For the purposes of counting rows and columns in this function
1261    only, horizontal rules are considered rows and vertical rules are
1262    considered columns.
1263
1264    FIXME: Doesn't use r1?  Huh?  */
1265 static int
1266 render_strip (int x, int y, int r, int c1, int c2, int r1 UNUSED, int r2)
1267 {
1268   int x_origin = x;
1269
1270   /* Horizontal rules. */
1271   if ((r & 1) == 0)
1272     {
1273       int hrh = t->hrh[r / 2];
1274       int c;
1275
1276       for (c = c1; c < c2; c++)
1277         {
1278           if (c & 1)
1279             {
1280               int style = t->rh[(c / 2) + (r / 2 * t->cf)];
1281
1282               if (style != TAL_0)
1283                 {
1284                   const struct color clr = {0, 0, 0, 0};
1285                   struct rect rct;
1286
1287                   rct.x1 = x;
1288                   rct.y1 = y;
1289                   rct.x2 = x + t->w[c / 2];
1290                   rct.y2 = y + hrh;
1291                   d->class->line_horz (d, &rct, &clr, style);
1292                 }
1293               x += t->w[c / 2];
1294             } else {
1295               const struct color clr = {0, 0, 0, 0};
1296               struct rect rct;
1297               struct outp_styles s;
1298
1299               rct.x1 = x;
1300               rct.y1 = y;
1301               rct.x2 = x + t->wrv[c / 2];
1302               rct.y2 = y + hrh;
1303
1304               s.t = r > 0 ? t->rv[(c / 2) + (t->cf + 1) * (r / 2 - 1)] : 0;
1305               s.b = r < 2 * t->nr ? t->rv[(c / 2) + (t->cf + 1) * (r / 2)] : 0;
1306               s.l = c > 0 ? t->rh[(c / 2 - 1) + t->cf * (r / 2)] : 0;
1307               s.r = c < 2 * t->nc ? t->rh[(c / 2) + t->cf * (r / 2)] : 0;
1308
1309               if (s.t | s.b | s.l | s.r)
1310                 d->class->line_intersection (d, &rct, &clr, &s);
1311               
1312               x += t->wrv[c / 2];
1313             }
1314         }
1315     } else {
1316       int c;
1317
1318       for (c = c1; c < c2; c++)
1319         {
1320           if (c & 1)
1321             {
1322               const int index = (c / 2) + (r / 2 * t->cf);
1323
1324               if (!(t->ct[index] & TAB_JOIN))
1325                 {
1326                   struct outp_text text;
1327
1328                   text.options = ((t->ct[index] & OUTP_T_JUST_MASK)
1329                                   | OUTP_T_HORZ | OUTP_T_VERT);
1330                   if ((t->ct[index] & TAB_EMPTY) == 0)
1331                     {
1332                       text.s = t->cc[index];
1333                       assert (!ls_null_p (&text.s));
1334                       text.h = t->w[c / 2];
1335                       text.v = t->h[r / 2];
1336                       text.x = x;
1337                       text.y = y;
1338                       d->class->text_draw (d, &text);
1339                     }
1340                 } else {
1341                   struct tab_joined_cell *j =
1342                     (struct tab_joined_cell *) ls_value (&t->cc[index]);
1343
1344                   if (j->hit != tab_hit)
1345                     {
1346                       j->hit = tab_hit;
1347
1348                       if (j->x1 == c / 2 && j->y1 == r / 2)
1349                         {
1350                           struct outp_text text;
1351
1352                           text.options = ((t->ct[index] & OUTP_T_JUST_MASK)
1353                                           | OUTP_T_HORZ | OUTP_T_VERT);
1354                           text.s = j->contents;
1355                           text.x = x;
1356                           text.y = y;
1357                           
1358                           {
1359                             int c;
1360
1361                             for (c = j->x1, text.h = -t->wrv[j->x2];
1362                                  c < j->x2 && c < c2 / 2; c++) 
1363                                 text.h += t->w[c] + t->wrv[c + 1]; 
1364                           }
1365                           
1366                           {
1367                             int r;
1368
1369                             for (r = j->y1, text.v = -t->hrh[j->y2];
1370                                  r < j->y2 && r < r2 / 2; r++)
1371                               text.v += t->h[r] + t->hrh[r + 1];
1372                           }
1373                           d->class->text_draw (d, &text);
1374                         }
1375                     }
1376                 }
1377               x += t->w[c / 2];
1378             } else {
1379               int style = t->rv[(c / 2) + (r / 2 * (t->cf + 1))];
1380
1381               if (style != TAL_0)
1382                 {
1383                   const struct color clr = {0, 0, 0, 0};
1384                   struct rect rct;
1385
1386                   rct.x1 = x;
1387                   rct.y1 = y;
1388                   rct.x2 = x + t->wrv[c / 2];
1389                   rct.y2 = y + t->h[r / 2];
1390                   d->class->line_vert (d, &rct, &clr, style);
1391                 }
1392               x += t->wrv[c / 2];
1393             }
1394         }
1395     }
1396
1397   return x - x_origin;
1398 }
1399