(render_strip) Fix bug that sometimes caused joined text in joined
[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   f.type = FMT_F;
616   f.w = w;
617   f.d = d;
618   
619 #if GLOBAL_DEBUGGING
620   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
621       || c + table->col_ofs >= table->nc
622       || r + table->row_ofs >= table->nr)
623     {
624       printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
625               "(%d,%d)\n",
626               c, table->col_ofs, c + table->col_ofs,
627               r, table->row_ofs, r + table->row_ofs,
628               table->nc, table->nr);
629       return;
630     }
631 #endif
632
633   data_out (buf, &f, (union value *) &val);
634   cp = buf;
635   while (isspace ((unsigned char) *cp) && cp < &buf[w])
636     cp++;
637   f.w = w - (cp - buf);
638
639   contents = pool_alloc (table->container, f.w);
640   ls_init (&table->cc[c + r * table->cf], contents, f.w);
641   table->ct[c + r * table->cf] = opt;
642   memcpy (contents, cp, f.w);
643 }
644
645 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
646    TEXT. */
647 void
648 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
649 {
650   va_list args;
651
652   assert (table != NULL && text != NULL);
653
654   assert (c >= 0 );
655   assert (r >= 0 );
656   assert (c < table->nc);
657   assert (r < table->nr);
658   
659
660 #if GLOBAL_DEBUGGING
661   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
662       || c + table->col_ofs >= table->nc
663       || r + table->row_ofs >= table->nr)
664     {
665       printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
666               "(%d,%d)\n",
667               c, table->col_ofs, c + table->col_ofs,
668               r, table->row_ofs, r + table->row_ofs,
669               table->nc, table->nr);
670       return;
671     }
672 #endif
673     
674   va_start (args, text);
675   text_format (table, opt, text, args, &table->cc[c + r * table->cf]);
676   table->ct[c + r * table->cf] = opt;
677   va_end (args);
678 }
679
680 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
681    options OPT to have text value TEXT. */
682 void
683 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
684                 unsigned opt, const char *text, ...)
685 {
686   struct tab_joined_cell *j;
687
688   assert (table != NULL && text != NULL);
689
690   assert (x1 >= 0);
691   assert (y1 >= 0);
692   assert (y2 >= y1);
693   assert (x2 >= x1);
694   assert (y2 < table->nr);
695   assert (x2 < table->nc);
696
697 #if GLOBAL_DEBUGGING
698   if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
699       || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
700       || x2 < x1 || x2 + table->col_ofs >= table->nc
701       || y2 < y2 || y2 + table->row_ofs >= table->nr)
702     {
703       printf ("tab_joint_text(): bad cell "
704               "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
705               x1, table->col_ofs, x1 + table->col_ofs,
706               y1, table->row_ofs, y1 + table->row_ofs,
707               x2, table->col_ofs, x2 + table->col_ofs,
708               y2, table->row_ofs, y2 + table->row_ofs,
709               table->nc, table->nr);
710       return;
711     }
712 #endif
713   
714   j = pool_alloc (table->container, sizeof *j);
715   j->hit = 0;
716   j->x1 = x1 + table->col_ofs;
717   j->y1 = y1 + table->row_ofs;
718   j->x2 = ++x2 + table->col_ofs;
719   j->y2 = ++y2 + table->row_ofs;
720   
721   {
722     va_list args;
723     
724     va_start (args, text);
725     text_format (table, opt, text, args, &j->contents);
726     va_end (args);
727   }
728   
729   opt |= TAB_JOIN;
730   
731   {
732     struct len_string *cc = &table->cc[x1 + y1 * table->cf];
733     unsigned char *ct = &table->ct[x1 + y1 * table->cf];
734     const int ofs = table->cf - (x2 - x1);
735
736     int y;
737     
738     for (y = y1; y < y2; y++)
739       {
740         int x;
741         
742         for (x = x1; x < x2; x++)
743           {
744             ls_init (cc++, (char *) j, 0);
745             *ct++ = opt;
746           }
747         
748         cc += ofs;
749         ct += ofs;
750       }
751   }
752 }
753
754 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
755 void
756 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
757          struct len_string *string)
758 {
759   assert (table != NULL && string != NULL);
760   
761 #if GLOBAL_DEBUGGING
762   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
763       || c + table->col_ofs >= table->nc
764       || r + table->row_ofs >= table->nr)
765     {
766       printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
767               "(%d,%d)\n",
768               c, table->col_ofs, c + table->col_ofs,
769               r, table->row_ofs, r + table->row_ofs,
770               table->nc, table->nr);
771       return;
772     }
773 #endif
774
775   table->cc[c + r * table->cf] = *string;
776   table->ct[c + r * table->cf] = opt;
777 }
778 \f
779 /* Miscellaneous. */
780
781 /* Sets the widths of all the columns and heights of all the rows in
782    table T for driver D. */
783 static void
784 nowrap_dim (struct tab_table *t, struct outp_driver *d)
785 {
786   t->w[0] = tab_natural_width (t, d, 0);
787   t->h[0] = d->font_height;
788 }
789
790 /* Sets the widths of all the columns and heights of all the rows in
791    table T for driver D. */
792 static void
793 wrap_dim (struct tab_table *t, struct outp_driver *d)
794 {
795   t->w[0] = tab_natural_width (t, d, 0);
796   t->h[0] = tab_natural_height (t, d, 0);
797 }
798
799 /* Outputs text BUF as a table with a single cell having cell options
800    OPTIONS, which is a combination of the TAB_* and TAT_*
801    constants. */
802 void
803 tab_output_text (int options, const char *buf, ...)
804 {
805   struct tab_table *t = tab_create (1, 1, 0);
806
807   assert (buf != NULL);
808   if (options & TAT_PRINTF)
809     {
810       va_list args;
811       char *temp_buf = local_alloc (4096);
812       
813       va_start (args, buf);
814       nvsprintf (temp_buf, buf, args);
815       buf = temp_buf;
816       va_end (args);
817     }
818   
819   if (options & TAT_FIX)
820     {
821       struct outp_driver *d;
822
823       for (d = outp_drivers (NULL); d; d = outp_drivers (d))
824         {
825           if (!d->page_open)
826             d->class->open_page (d);
827
828           if (d->class->text_set_font_by_name != NULL)
829             d->class->text_set_font_by_name (d, "FIXED");
830           else 
831             {
832               /* FIXME */
833             }
834         }
835     }
836
837   tab_text (t, 0, 0, options &~ TAT_PRINTF, buf);
838   tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
839   if (options & TAT_NOWRAP)
840     tab_dim (t, nowrap_dim);
841   else
842     tab_dim (t, wrap_dim);
843   tab_submit (t);
844
845   if (options & TAT_FIX)
846     {
847       struct outp_driver *d;
848
849       for (d = outp_drivers (NULL); d; d = outp_drivers (d))
850         if (d->class->text_set_font_by_name != NULL)
851           d->class->text_set_font_by_name (d, "PROP");
852         else 
853           {
854             /* FIXME */
855           }
856     }
857   
858   if (options & TAT_PRINTF)
859     local_free (buf);
860 }
861
862 /* Set table flags to FLAGS. */
863 void
864 tab_flags (struct tab_table *t, unsigned flags)
865 {
866   assert (t != NULL);
867   t->flags = flags;
868 }
869
870 /* Easy, type-safe way to submit a tab table to som. */
871 void
872 tab_submit (struct tab_table *t)
873 {
874   struct som_table s;
875
876   assert (t != NULL);
877   s.class = &tab_table_class;
878   s.ext = t;
879   som_submit (&s);
880   tab_destroy (t);
881 }
882 \f
883 /* Editing. */
884
885 /* Set table row and column offsets for all functions that affect
886    cells or rules. */
887 void
888 tab_offset (struct tab_table *t, int col, int row)
889 {
890   int diff = 0;
891
892   assert (t != NULL);
893 #if GLOBAL_DEBUGGING
894   if (row < -1 || row >= t->nr)
895     {
896       printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
897       abort ();
898     }
899   if (col < -1 || col >= t->nc)
900     {
901       printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
902       abort ();
903     }
904 #endif
905
906   if (row != -1)
907     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
908   if (col != -1)
909     diff += (col - t->col_ofs), t->col_ofs = col;
910
911   t->cc += diff;
912   t->ct += diff;
913 }
914
915 /* Increment the row offset by one. If the table is too small,
916    increase its size. */
917 void
918 tab_next_row (struct tab_table *t)
919 {
920   assert (t != NULL);
921   t->cc += t->cf;
922   t->ct += t->cf;
923   if (++t->row_ofs >= t->nr)
924     tab_realloc (t, -1, t->nr * 4 / 3);
925 }
926 \f
927 static struct tab_table *t;
928 static struct outp_driver *d;
929 int tab_hit;
930
931 /* Set the current table to TABLE. */
932 static void
933 tabi_table (struct som_table *table)
934 {
935   assert (table != NULL);
936   t = table->ext;
937   tab_offset (t, 0, 0);
938   
939   assert (t->w == NULL && t->h == NULL);
940   t->w = pool_alloc (t->container, sizeof *t->w * t->nc);
941   t->h = pool_alloc (t->container, sizeof *t->h * t->nr);
942 }
943
944 /* Set the current output device to DRIVER. */
945 static void
946 tabi_driver (struct outp_driver *driver)
947 {
948   int i;
949
950   assert (driver != NULL);
951   d = driver;
952   
953   /* Figure out sizes of rules. */
954   for (t->hr_tot = i = 0; i <= t->nr; i++)
955     t->hr_tot += t->hrh[i] = d->horiz_line_spacing[t->trh[i]];
956   for (t->vr_tot = i = 0; i <= t->nc; i++)
957     t->vr_tot += t->wrv[i] = d->vert_line_spacing[t->trv[i]];
958
959 #if GLOBAL_DEBUGGING
960   for (i = 0; i < t->nr; i++)
961     t->h[i] = -1;
962   for (i = 0; i < t->nc; i++)
963     t->w[i] = -1;
964 #endif
965
966   assert (t->dim != NULL);
967   t->dim (t, d);
968
969 #if GLOBAL_DEBUGGING
970   {
971     int error = 0;
972
973     for (i = 0; i < t->nr; i++)
974       {
975         if (t->h[i] == -1)
976           {
977             printf ("Table row %d height not initialized.\n", i);
978             error = 1;
979           }
980         assert (t->h[i] > 0);
981       }
982     
983     for (i = 0; i < t->nc; i++)
984       {
985         if (t->w[i] == -1)
986           {
987             printf ("Table column %d width not initialized.\n", i);
988             error = 1;
989           }
990         assert (t->w[i] > 0);
991       }
992   }
993 #endif
994     
995   /* Add up header sizes. */
996   for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
997     t->wl += t->w[i] + t->wrv[i + 1];
998   for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
999     t->ht += t->h[i] + t->hrh[i + 1];
1000   for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
1001     t->wr += t->w[i] + t->wrv[i + 1];
1002   for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
1003     t->hb += t->h[i] + t->hrh[i + 1];
1004   
1005   /* Title. */
1006   if (!(t->flags & SOMF_NO_TITLE))
1007     t->ht += d->font_height;
1008 }
1009
1010 /* Return the number of columns and rows in the table into N_COLUMNS
1011    and N_ROWS, respectively. */
1012 static void
1013 tabi_count (int *n_columns, int *n_rows)
1014 {
1015   assert (n_columns != NULL && n_rows != NULL);
1016   *n_columns = t->nc;
1017   *n_rows = t->nr;
1018 }
1019
1020 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
1021
1022 /* Return the horizontal and vertical size of the entire table,
1023    including headers, for the current output device, into HORIZ and
1024    VERT. */
1025 static void
1026 tabi_area (int *horiz, int *vert)
1027 {
1028   assert (horiz != NULL && vert != NULL);
1029   
1030   {
1031     int w, c;
1032     
1033     for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
1034          c < t->nc - t->r; c++)
1035       w += t->w[c] + t->wrv[c];
1036     *horiz = w;
1037   }
1038   
1039   {
1040     int h, r;
1041     for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1042          r < t->nr - t->b; r++)
1043       h += t->h[r] + t->hrh[r];
1044     *vert = h;
1045   }
1046 }
1047
1048 /* Return the column style for this table into STYLE. */
1049 static void
1050 tabi_columns (int *style)
1051 {
1052   assert (style != NULL);
1053   *style = t->col_style;
1054 }
1055
1056 /* Return the number of header rows/columns on the left, right, top,
1057    and bottom sides into HL, HR, HT, and HB, respectively. */
1058 static void
1059 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1060 {
1061   assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1062   *hl = t->l;
1063   *hr = t->r;
1064   *ht = t->t;
1065   *hb = t->b;
1066 }
1067
1068 /* Determines the number of rows or columns (including appropriate
1069    headers), depending on CUMTYPE, that will fit into the space
1070    specified.  Takes rows/columns starting at index START and attempts
1071    to fill up available space MAX.  Returns in END the index of the
1072    last row/column plus one; returns in ACTUAL the actual amount of
1073    space the selected rows/columns (including appropriate headers)
1074    filled. */
1075 static void
1076 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1077 {
1078   int n;
1079   int *d;
1080   int *r;
1081   int total;
1082   
1083   assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1084   if (cumtype == SOM_ROWS)
1085     {
1086       assert (start >= 0 && start < t->nr);
1087       n = t->nr - t->b;
1088       d = &t->h[start];
1089       r = &t->hrh[start + 1];
1090       total = t->ht + t->hb;
1091     } else {
1092       assert (start >= 0 && start < t->nc);
1093       n = t->nc - t->r;
1094       d = &t->w[start];
1095       r = &t->wrv[start + 1];
1096       total = t->wl + t->wr;
1097     }
1098   
1099   total += *d++;
1100   if (total > max)
1101     {
1102       if (end)
1103         *end = start;
1104       if (actual)
1105         *actual = 0;
1106       return;
1107     }
1108     
1109   {
1110     int x;
1111       
1112     for (x = start + 1; x < n; x++)
1113       {
1114         int amt = *d++ + *r++;
1115         
1116         total += amt;
1117         if (total > max)
1118           {
1119             total -= amt;
1120             break;
1121           }
1122       }
1123
1124     if (end)
1125       *end = x;
1126     
1127     if (actual)
1128       *actual = total;
1129   }
1130 }
1131
1132 /* Return flags set for the current table into FLAGS. */
1133 static void
1134 tabi_flags (unsigned *flags)
1135 {
1136   assert (flags != NULL);
1137   *flags = t->flags;
1138 }
1139
1140 /* Render title for current table, with major index X and minor index
1141    Y.  Y may be zero, or X and Y may be zero, but X should be nonzero
1142    if Y is nonzero. */
1143 static void
1144 tabi_title (int x, int y)
1145 {
1146   char buf[1024];
1147   char *cp;
1148
1149   if (t->flags & SOMF_NO_TITLE)
1150     return;
1151   
1152   cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1153   if (x && y)
1154     cp = spprintf (cp, "(%d:%d)", x, y);
1155   else if (x)
1156     cp = spprintf (cp, "(%d)", x);
1157   if (cur_proc)
1158     cp = spprintf (cp, " %s", cur_proc);
1159   cp = stpcpy (cp, ".  ");
1160   if (!ls_empty_p (&t->title))
1161     {
1162       memcpy (cp, ls_value (&t->title), ls_length (&t->title));
1163       cp += ls_length (&t->title);
1164     }
1165   *cp = 0;
1166   
1167   {
1168     struct outp_text text;
1169
1170     text.options = OUTP_T_JUST_LEFT | OUTP_T_HORZ | OUTP_T_VERT;
1171     ls_init (&text.s, buf, cp - buf);
1172     text.h = d->width;
1173     text.v = d->font_height;
1174     text.x = 0;
1175     text.y = d->cp_y;
1176     d->class->text_draw (d, &text);
1177   }
1178 }
1179
1180 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1181
1182 /* Draws the table region in rectangle (X1,Y1)-(X2,Y2), where column
1183    X2 and row Y2 are not included in the rectangle, at the current
1184    position on the current output device.  Draws headers as well. */
1185 static void
1186 tabi_render (int x1, int y1, int x2, int y2)
1187 {
1188   int i, y;
1189   int ranges[3][2];
1190   
1191   tab_hit++;
1192
1193   y = d->cp_y;
1194   if (!(t->flags & SOMF_NO_TITLE))
1195     y += d->font_height;
1196
1197   /* Top headers. */
1198   ranges[0][0] = 0;
1199   ranges[0][1] = t->t * 2 + 1;
1200
1201   /* Requested rows. */
1202   ranges[1][0] = y1 * 2 + 1;
1203   ranges[1][1] = y2 * 2;
1204
1205   /* Bottom headers. */
1206   ranges[2][0] = (t->nr - t->b) * 2;
1207   ranges[2][1] = t->nr * 2 + 1;
1208
1209   for (i = 0; i < 3; i++) 
1210     {
1211       int r;
1212
1213       for (r = ranges[i][0]; r < ranges[i][1]; r++) 
1214         {
1215           int x = d->cp_x;
1216           x += render_strip (x, y, r, 0, t->l * 2 + 1, y1, y2);
1217           x += render_strip (x, y, r, x1 * 2 + 1, x2 * 2, y1, y2);
1218           x += render_strip (x, y, r, (t->nc - t->r) * 2,
1219                              t->nc * 2 + 1, y1, y2);
1220           y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2]; 
1221         }
1222     }
1223 }
1224
1225 struct som_table_class tab_table_class =
1226   {
1227     tabi_table,
1228     tabi_driver,
1229     
1230     tabi_count,
1231     tabi_area,
1232     NULL,
1233     NULL,
1234     tabi_columns,
1235     NULL,
1236     tabi_headers,
1237     NULL,
1238     tabi_cumulate,
1239     tabi_flags,
1240     
1241     NULL,
1242     NULL,
1243
1244     tabi_title,
1245     tabi_render,
1246   };
1247 \f
1248 /* Render contiguous strip consisting of columns C1...C2, exclusive,
1249    on row R, at location (X,Y).  Return width of the strip thus
1250    rendered.
1251
1252    Renders joined cells, even those outside the strip, within the
1253    rendering region (C1,R1)-(C2,R2).
1254
1255    For the purposes of counting rows and columns in this function
1256    only, horizontal rules are considered rows and vertical rules are
1257    considered columns.
1258
1259    FIXME: Doesn't use r1?  Huh?  */
1260 static int
1261 render_strip (int x, int y, int r, int c1, int c2, int r1 unused, int r2)
1262 {
1263   int x_origin = x;
1264
1265   /* Horizontal rules. */
1266   if ((r & 1) == 0)
1267     {
1268       int hrh = t->hrh[r / 2];
1269       int c;
1270
1271       for (c = c1; c < c2; c++)
1272         {
1273           if (c & 1)
1274             {
1275               int style = t->rh[(c / 2) + (r / 2 * t->cf)];
1276
1277               if (style != TAL_0)
1278                 {
1279                   const struct color clr = {0, 0, 0, 0};
1280                   struct rect rct;
1281
1282                   rct.x1 = x;
1283                   rct.y1 = y;
1284                   rct.x2 = x + t->w[c / 2];
1285                   rct.y2 = y + hrh;
1286                   d->class->line_horz (d, &rct, &clr, style);
1287                 }
1288               x += t->w[c / 2];
1289             } else {
1290               const struct color clr = {0, 0, 0, 0};
1291               struct rect rct;
1292               struct outp_styles s;
1293
1294               rct.x1 = x;
1295               rct.y1 = y;
1296               rct.x2 = x + t->wrv[c / 2];
1297               rct.y2 = y + hrh;
1298
1299               s.t = r > 0 ? t->rv[(c / 2) + (t->cf + 1) * (r / 2 - 1)] : 0;
1300               s.b = r < 2 * t->nr ? t->rv[(c / 2) + (t->cf + 1) * (r / 2)] : 0;
1301               s.l = c > 0 ? t->rh[(c / 2 - 1) + t->cf * (r / 2)] : 0;
1302               s.r = c < 2 * t->nc ? t->rh[(c / 2) + t->cf * (r / 2)] : 0;
1303
1304               if (s.t | s.b | s.l | s.r)
1305                 d->class->line_intersection (d, &rct, &clr, &s);
1306               
1307               x += t->wrv[c / 2];
1308             }
1309         }
1310     } else {
1311       int c;
1312
1313       for (c = c1; c < c2; c++)
1314         {
1315           if (c & 1)
1316             {
1317               const int index = (c / 2) + (r / 2 * t->cf);
1318
1319               if (!(t->ct[index] & TAB_JOIN))
1320                 {
1321                   struct outp_text text;
1322
1323                   text.options = ((t->ct[index] & OUTP_T_JUST_MASK)
1324                                   | OUTP_T_HORZ | OUTP_T_VERT);
1325                   if ((t->ct[index] & TAB_EMPTY) == 0)
1326                     {
1327                       text.s = t->cc[index];
1328                       assert (!ls_null_p (&text.s));
1329                       text.h = t->w[c / 2];
1330                       text.v = t->h[r / 2];
1331                       text.x = x;
1332                       text.y = y;
1333                       d->class->text_draw (d, &text);
1334                     }
1335                 } else {
1336                   struct tab_joined_cell *j =
1337                     (struct tab_joined_cell *) ls_value (&t->cc[index]);
1338
1339                   if (j->hit != tab_hit)
1340                     {
1341                       j->hit = tab_hit;
1342
1343                       if (j->x1 == c / 2 && j->y1 == r / 2)
1344                         {
1345                           struct outp_text text;
1346
1347                           text.options = ((t->ct[index] & OUTP_T_JUST_MASK)
1348                                           | OUTP_T_HORZ | OUTP_T_VERT);
1349                           text.s = j->contents;
1350                           text.x = x;
1351                           text.y = y;
1352                           
1353                           {
1354                             int c;
1355
1356                             for (c = j->x1, text.h = -t->wrv[j->x2];
1357                                  c < j->x2 && c < c2 / 2; c++) 
1358                                 text.h += t->w[c] + t->wrv[c + 1]; 
1359                           }
1360                           
1361                           {
1362                             int r;
1363
1364                             for (r = j->y1, text.v = -t->hrh[j->y2];
1365                                  r < j->y2 && r < r2 / 2; r++)
1366                               text.v += t->h[r] + t->hrh[r + 1];
1367                           }
1368                           d->class->text_draw (d, &text);
1369                         }
1370                     }
1371                 }
1372               x += t->w[c / 2];
1373             } else {
1374               int style = t->rv[(c / 2) + (r / 2 * (t->cf + 1))];
1375
1376               if (style != TAL_0)
1377                 {
1378                   const struct color clr = {0, 0, 0, 0};
1379                   struct rect rct;
1380
1381                   rct.x1 = x;
1382                   rct.y1 = y;
1383                   rct.x2 = x + t->wrv[c / 2];
1384                   rct.y2 = y + t->h[r / 2];
1385                   d->class->line_vert (d, &rct, &clr, style);
1386                 }
1387               x += t->wrv[c / 2];
1388             }
1389         }
1390     }
1391
1392   return x - x_origin;
1393 }
1394