201f0d8221c45f29c3c96391577299561a0dc242
[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 <stdarg.h>
24 #include <limits.h>
25 #include <stdlib.h>
26 #include "error.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 /* Creates a table with NC columns and NR rows.  If REALLOCABLE is
42    nonzero then the table's size can be increased later; otherwise,
43    its size can only be reduced. */
44 struct tab_table *
45 tab_create (int nc, int nr, int reallocable)
46 {
47   void *(*alloc_func) (struct pool *, size_t);
48
49   struct tab_table *t;
50   
51   {
52     struct pool *container = pool_create ();
53     t = pool_alloc (container, sizeof *t);
54     t->container = container;
55   }
56   
57   t->col_style = TAB_COL_NONE;
58   t->col_group = 0;
59   ls_null (&t->title);
60   t->flags = SOMF_NONE;
61   t->nr = nr;
62   t->nc = t->cf = nc;
63   t->l = t->r = t->t = t->b = 0;
64
65   alloc_func = reallocable ? pool_malloc : pool_alloc;
66 #if GLOBAL_DEBUGGING
67   t->reallocable = reallocable;
68 #endif
69
70   t->cc = alloc_func (t->container, nr * nc * sizeof *t->cc);
71   t->ct = alloc_func (t->container, nr * nc);
72   memset (t->ct, TAB_EMPTY, nc * nr);
73
74   t->rh = alloc_func (t->container, nc * (nr + 1));
75   memset (t->rh, 0, nc * (nr + 1));
76
77   t->hrh = alloc_func (t->container, sizeof *t->hrh * (nr + 1));
78   memset (t->hrh, 0, sizeof *t->hrh * (nr + 1));
79
80   t->trh = alloc_func (t->container, nr + 1);
81   memset (t->trh, 0, nr + 1);
82
83   t->rv = alloc_func (t->container, (nc + 1) * nr);
84   memset (t->rv, 0, (nc + 1) * nr);
85
86   t->wrv = alloc_func (t->container, sizeof *t->wrv * (nc + 1));
87   memset (t->wrv, 0, sizeof *t->wrv * (nc + 1));
88
89   t->trv = alloc_func (t->container, nc + 1);
90   memset (t->trv, 0, nc + 1);
91
92   t->dim = NULL;
93   t->w = t->h = NULL;
94   t->col_ofs = t->row_ofs = 0;
95   
96   return t;
97 }
98
99 /* Destroys table T. */
100 void
101 tab_destroy (struct tab_table *t)
102 {
103   assert (t != NULL);
104   pool_destroy (t->container);
105   t=0;
106 }
107
108 /* Sets the width and height of a table, in columns and rows,
109    respectively.  Use only to reduce the size of a table, since it
110    does not change the amount of allocated memory. */
111 void
112 tab_resize (struct tab_table *t, int nc, int nr)
113 {
114   assert (t != NULL);
115   if (nc != -1)
116     {
117       assert (nc + t->col_ofs <= t->cf);
118       t->nc = nc + t->col_ofs;
119     }
120   if (nr != -1)
121     {
122       assert (nr + t->row_ofs <= t->nr);
123       t->nr = nr + t->row_ofs;
124     }
125 }
126
127 /* Changes either or both dimensions of a table.  Consider using the
128    above routine instead if it won't waste a lot of space.
129
130    Changing the number of columns in a table is particularly expensive
131    in space and time.  Avoid doing such.  FIXME: In fact, transferring
132    of rules isn't even implemented yet. */
133 void
134 tab_realloc (struct tab_table *t, int nc, int nr)
135 {
136   int ro, co;
137   
138   assert (t != NULL);
139 #if GLOBAL_DEBUGGING
140   assert (t->reallocable);
141 #endif
142   ro = t->row_ofs;
143   co = t->col_ofs;
144   if (ro || co)
145     tab_offset (t, 0, 0);
146
147   if (nc == -1)
148     nc = t->nc;
149   if (nr == -1)
150     nr = t->nr;
151   
152   assert (nc == t->nc);
153   
154   if (nc > t->cf)
155     {
156       int mr1 = min (nr, t->nr);
157       int mc1 = min (nc, t->nc);
158       
159       struct len_string *new_cc;
160       unsigned char *new_ct;
161       int r;
162
163       new_cc = pool_malloc (t->container, nr * nc * sizeof *new_cc);
164       new_ct = pool_malloc (t->container, nr * nc);
165       for (r = 0; r < mr1; r++)
166         {
167           memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
168           memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
169           memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
170         }
171       pool_free (t->container, t->cc);
172       pool_free (t->container, t->ct);
173       t->cc = new_cc;
174       t->ct = new_ct;
175       t->cf = nc;
176     }
177   else if (nr != t->nr)
178     {
179       t->cc = pool_realloc (t->container, t->cc, nr * nc * sizeof *t->cc);
180       t->ct = pool_realloc (t->container, t->ct, nr * nc);
181
182       t->rh = pool_realloc (t->container, t->rh, nc * (nr + 1));
183       t->rv = pool_realloc (t->container, t->rv, (nc + 1) * nr);
184       t->trh = pool_realloc (t->container, t->trh, nr + 1);
185       t->hrh = pool_realloc (t->container, t->hrh,
186                              sizeof *t->hrh * (nr + 1));
187       
188       if (nr > t->nr)
189         {
190           memset (&t->rh[nc * (t->nr + 1)], 0, (nr - t->nr) * nc);
191           memset (&t->rv[(nc + 1) * t->nr], 0, (nr - t->nr) * (nc + 1));
192           memset (&t->trh[t->nr + 1], 0, nr - t->nr);
193         }
194     }
195
196   memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
197   
198   t->nr = nr;
199   t->nc = nc;
200
201   if (ro || co)
202     tab_offset (t, co, ro);
203 }
204
205 /* Sets the number of header rows on each side of TABLE to L on the
206    left, R on the right, T on the top, B on the bottom.  Header rows
207    are repeated when a table is broken across multiple columns or
208    multiple pages. */
209 void
210 tab_headers (struct tab_table *table, int l, int r, int t, int b)
211 {
212   assert (table != NULL);
213   assert (l < table->nc);
214   assert (r < table->nc);
215   assert (t < table->nr);
216   assert (b < table->nr);
217
218
219   table->l = l;
220   table->r = r;
221   table->t = t;
222   table->b = b;
223 }
224
225 /* Set up table T so that, when it is an appropriate size, it will be
226    displayed across the page in columns.
227
228    STYLE is a TAB_COL_* constant.  GROUP is the number of rows to take
229    as a unit. */
230 void
231 tab_columns (struct tab_table *t, int style, int group)
232 {
233   assert (t != NULL);
234   t->col_style = style;
235   t->col_group = group;
236 }
237 \f
238 /* Rules. */
239
240 /* Draws a vertical line to the left of cells at horizontal position X
241    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
242 void
243 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
244 {
245   int y;
246
247   assert (t != NULL);
248
249 #if GLOBAL_DEBUGGING
250   if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
251       || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
252       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
253     {
254       printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
255                 "table size (%d,%d)\n"),
256               x, t->col_ofs, x + t->col_ofs,
257               y1, t->row_ofs, y1 + t->row_ofs,
258               y2, t->row_ofs, y2 + t->row_ofs,
259               t->nc, t->nr);
260       return;
261     }
262 #endif
263
264   x += t->col_ofs;
265   y1 += t->row_ofs;
266   y2 += t->row_ofs;
267
268   assert (x  > 0);
269   assert (x  < t->nc);
270   assert (y1 >= 0);
271   assert (y2 >= y1);
272   assert (y2 <=  t->nr);
273
274   if (style != -1)
275     {
276       if ((style & TAL_SPACING) == 0)
277         for (y = y1; y <= y2; y++)
278           t->rv[x + (t->cf + 1) * y] = style;
279       t->trv[x] |= (1 << (style & ~TAL_SPACING));
280     }
281 }
282
283 /* Draws a horizontal line above cells at vertical position Y from X1
284    to X2 inclusive in style STYLE, if style is not -1. */
285 void
286 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
287 {
288   int x;
289
290   assert (t != NULL);
291
292   x1 += t->col_ofs;
293   x2 += t->col_ofs;
294   y += t->row_ofs;
295
296   assert (y >= 0);
297   assert (y < t->nr);
298   assert (x2 >= x1 );
299   assert (x1 >= 0 );
300   assert (x2 < t->nc);
301
302   if (style != -1)
303     {
304       if ((style & TAL_SPACING) == 0)
305         for (x = x1; x <= x2; x++)
306           t->rh[x + t->cf * y] = style;
307       t->trh[y] |= (1 << (style & ~TAL_SPACING));
308     }
309 }
310
311 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
312    lines of style F_H and vertical lines of style F_V.  Fills the
313    interior of the box with horizontal lines of style I_H and vertical
314    lines of style I_V.  Any of the line styles may be -1 to avoid
315    drawing those lines.  This is distinct from 0, which draws a null
316    line. */
317 void
318 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
319          int x1, int y1, int x2, int y2)
320 {
321   assert (t != NULL);
322
323 #if GLOBAL_DEBUGGING
324   if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc 
325       || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
326       || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr 
327       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
328     {
329       printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
330                 "in table size (%d,%d)\n"),
331               x1, t->col_ofs, x1 + t->col_ofs,
332               y1, t->row_ofs, y1 + t->row_ofs,
333               x2, t->col_ofs, x2 + t->col_ofs,
334               y2, t->row_ofs, y2 + t->row_ofs,
335               t->nc, t->nr);
336       abort ();
337     }
338 #endif
339
340   x1 += t->col_ofs;
341   x2 += t->col_ofs;
342   y1 += t->row_ofs;
343   y2 += t->row_ofs;
344
345   assert (x2 >= x1);
346   assert (y2 >= y1);
347   assert (x1 >= 0);
348   assert (y1 >= 0);
349   assert (x2 < t->nc);
350   assert (y2 < t->nr);
351
352   if (f_h != -1)
353     {
354       int x;
355       if ((f_h & TAL_SPACING) == 0)
356         for (x = x1; x <= x2; x++)
357           {
358             t->rh[x + t->cf * y1] = f_h;
359             t->rh[x + t->cf * (y2 + 1)] = f_h;
360           }
361       t->trh[y1] |= (1 << (f_h & ~TAL_SPACING));
362       t->trh[y2 + 1] |= (1 << (f_h & ~TAL_SPACING));
363     }
364   if (f_v != -1)
365     {
366       int y;
367       if ((f_v & TAL_SPACING) == 0)
368         for (y = y1; y <= y2; y++)
369           {
370             t->rv[x1 + (t->cf + 1) * y] = f_v;
371             t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
372           }
373       t->trv[x1] |= (1 << (f_v & ~TAL_SPACING));
374       t->trv[x2 + 1] |= (1 << (f_v & ~TAL_SPACING));
375     }
376
377   if (i_h != -1)
378     {
379       int y;
380       
381       for (y = y1 + 1; y <= y2; y++)
382         {
383           int x;
384
385           if ((i_h & TAL_SPACING) == 0)
386             for (x = x1; x <= x2; x++)
387               t->rh[x + t->cf * y] = i_h;
388
389           t->trh[y] |= (1 << (i_h & ~TAL_SPACING));
390         }
391     }
392   if (i_v != -1)
393     {
394       int x;
395       
396       for (x = x1 + 1; x <= x2; x++)
397         {
398           int y;
399           
400           if ((i_v & TAL_SPACING) == 0)
401             for (y = y1; y <= y2; y++)
402               t->rv[x + (t->cf + 1) * y] = i_v;
403
404           t->trv[x] |= (1 << (i_v & ~TAL_SPACING));
405         }
406     }
407 }
408
409 /* Formats text TEXT and arguments ARGS as indicated in OPT and sets
410    the resultant string into S in TABLE's pool. */
411 static void
412 text_format (struct tab_table *table, int opt, const char *text, va_list args,
413              struct len_string *s)
414 {
415   int len;
416   
417   assert (table != NULL && text != NULL && s != NULL);
418   
419   if (opt & TAT_PRINTF)
420     {
421       char *temp_buf = local_alloc (1024);
422       
423       len = nvsprintf (temp_buf, text, args);
424       text = temp_buf;
425     }
426   else
427     len = strlen (text);
428
429   ls_create_buffer (s, text, len);
430   pool_register (table->container, free, s->string);
431   
432   if (opt & TAT_PRINTF)
433     local_free (text);
434 }
435
436 /* Set the title of table T to TITLE, which is formatted with printf
437    if FORMAT is nonzero. */
438 void
439 tab_title (struct tab_table *t, int format, const char *title, ...)
440 {
441   va_list args;
442
443   assert (t != NULL && title != NULL);
444   va_start (args, title);
445   text_format (t, format ? TAT_PRINTF : TAT_NONE, title, args, &t->title);
446   va_end (args);
447 }
448
449 /* Set DIM_FUNC as the dimension function for table T. */
450 void
451 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
452 {
453   assert (t != NULL && t->dim == NULL);
454   t->dim = dim_func;
455 }
456
457 /* Returns the natural width of column C in table T for driver D, that
458    is, the smallest width necessary to display all its cells without
459    wrapping.  The width will be no larger than the page width minus
460    left and right rule widths. */
461 int
462 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
463 {
464   int width;
465
466   assert (t != NULL && c >= 0 && c < t->nc);
467   {
468     int r;
469
470     for (width = r = 0; r < t->nr; r++)
471       {
472         struct outp_text text;
473         unsigned char opt = t->ct[c + r * t->cf];
474                 
475         if (opt & (TAB_JOIN | TAB_EMPTY))
476           continue;
477
478         text.s = t->cc[c + r * t->cf];
479         assert (!ls_null_p (&text.s));
480         text.options = OUTP_T_JUST_LEFT;
481
482         d->class->text_metrics (d, &text);
483         if (text.h > width)
484           width = text.h;
485       }
486   }
487
488   if (width == 0)
489     {
490       width = d->prop_em_width * 8;
491 #if GLOBAL_DEBUGGING
492       printf ("warning: table column %d contains no data.\n", c);
493 #endif
494     }
495   
496   {
497     const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
498     
499     if (width > clamp)
500       width = clamp;
501   }
502
503   return width;
504 }
505
506 /* Returns the natural height of row R in table T for driver D, that
507    is, the minimum height necessary to display the information in the
508    cell at the widths set for each column. */
509 int
510 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
511 {
512   int height;
513
514   assert (t != NULL && r >= 0 && r < t->nr);
515   
516   {
517     int c;
518     
519     for (height = d->font_height, c = 0; c < t->nc; c++)
520       {
521         struct outp_text text;
522         unsigned char opt = t->ct[c + r * t->cf];
523
524         assert (t->w[c] != NOT_INT);
525         if (opt & (TAB_JOIN | TAB_EMPTY))
526           continue;
527
528         text.s = t->cc[c + r * t->cf];
529         assert (!ls_null_p (&text.s));
530         text.options = OUTP_T_HORZ | OUTP_T_JUST_LEFT;
531         text.h = t->w[c];
532         d->class->text_metrics (d, &text);
533
534         if (text.v > height)
535           height = text.v;
536       }
537   }
538
539   return height;
540 }
541
542 /* Callback function to set all columns and rows to their natural
543    dimensions.  Not really meant to be called directly.  */
544 void
545 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
546 {
547   int i;
548
549   assert (t != NULL);
550   
551   for (i = 0; i < t->nc; i++)
552     t->w[i] = tab_natural_width (t, d, i);
553   
554   for (i = 0; i < t->nr; i++)
555     t->h[i] = tab_natural_height (t, d, i);
556 }
557
558 \f
559 /* Cells. */
560
561 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
562    from V, displayed with format spec F. */
563 void
564 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
565            const union value *v, const struct fmt_spec *f)
566 {
567   char *contents;
568
569   assert (table != NULL && v != NULL && f != NULL);
570 #if GLOBAL_DEBUGGING
571   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
572       || c + table->col_ofs >= table->nc
573       || r + table->row_ofs >= table->nr)
574     {
575       printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
576               "(%d,%d)\n",
577               c, table->col_ofs, c + table->col_ofs,
578               r, table->row_ofs, r + table->row_ofs,
579               table->nc, table->nr);
580       return;
581     }
582 #endif
583
584   contents = pool_alloc (table->container, f->w);
585   ls_init (&table->cc[c + r * table->cf], contents, f->w);
586   table->ct[c + r * table->cf] = opt;
587   
588   data_out (contents, f, v);
589 }
590
591 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
592    with NDEC decimal places. */
593 void
594 tab_float (struct tab_table *table, int c, int r, unsigned char opt,
595            double val, int w, int d)
596 {
597   char *contents;
598   char buf[40], *cp;
599   
600   struct fmt_spec f;
601   union value double_value;
602
603   assert (table != NULL && w <= 40);
604   
605   assert (c >= 0);
606   assert (c < table->nc);
607   assert (r >= 0);
608   assert (r < table->nr);
609
610   f.type = FMT_F;
611   f.w = w;
612   f.d = d;
613   
614 #if GLOBAL_DEBUGGING
615   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
616       || c + table->col_ofs >= table->nc
617       || r + table->row_ofs >= table->nr)
618     {
619       printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
620               "(%d,%d)\n",
621               c, table->col_ofs, c + table->col_ofs,
622               r, table->row_ofs, r + table->row_ofs,
623               table->nc, table->nr);
624       return;
625     }
626 #endif
627
628   double_value.f = val;
629   data_out (buf, &f, &double_value);
630
631   cp = buf;
632   while (isspace ((unsigned char) *cp) && cp < &buf[w])
633     cp++;
634   f.w = w - (cp - buf);
635
636   contents = pool_alloc (table->container, f.w);
637   ls_init (&table->cc[c + r * table->cf], contents, f.w);
638   table->ct[c + r * table->cf] = opt;
639   memcpy (contents, cp, f.w);
640 }
641
642 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
643    TEXT. */
644 void
645 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
646 {
647   va_list args;
648
649   assert (table != NULL && text != NULL);
650
651   assert (c >= 0 );
652   assert (r >= 0 );
653   assert (c < table->nc);
654   assert (r < table->nr);
655   
656
657 #if GLOBAL_DEBUGGING
658   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
659       || c + table->col_ofs >= table->nc
660       || r + table->row_ofs >= table->nr)
661     {
662       printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
663               "(%d,%d)\n",
664               c, table->col_ofs, c + table->col_ofs,
665               r, table->row_ofs, r + table->row_ofs,
666               table->nc, table->nr);
667       return;
668     }
669 #endif
670     
671   va_start (args, text);
672   text_format (table, opt, text, args, &table->cc[c + r * table->cf]);
673   table->ct[c + r * table->cf] = opt;
674   va_end (args);
675 }
676
677 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
678    options OPT to have text value TEXT. */
679 void
680 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
681                 unsigned opt, const char *text, ...)
682 {
683   struct tab_joined_cell *j;
684
685   assert (table != NULL && text != NULL);
686
687   assert (x1 + table->col_ofs >= 0);
688   assert (y1 + table->row_ofs >= 0);
689   assert (y2 >= y1);
690   assert (x2 >= x1);
691   assert (y2 + table->row_ofs < table->nr);
692   assert (x2 + table->col_ofs < table->nc);
693
694 #if GLOBAL_DEBUGGING
695   if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
696       || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
697       || x2 < x1 || x2 + table->col_ofs >= table->nc
698       || y2 < y2 || y2 + table->row_ofs >= table->nr)
699     {
700       printf ("tab_joint_text(): bad cell "
701               "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
702               x1, table->col_ofs, x1 + table->col_ofs,
703               y1, table->row_ofs, y1 + table->row_ofs,
704               x2, table->col_ofs, x2 + table->col_ofs,
705               y2, table->row_ofs, y2 + table->row_ofs,
706               table->nc, table->nr);
707       return;
708     }
709 #endif
710   
711   j = pool_alloc (table->container, sizeof *j);
712   j->hit = 0;
713   j->x1 = x1 + table->col_ofs;
714   j->y1 = y1 + table->row_ofs;
715   j->x2 = ++x2 + table->col_ofs;
716   j->y2 = ++y2 + table->row_ofs;
717   
718   {
719     va_list args;
720     
721     va_start (args, text);
722     text_format (table, opt, text, args, &j->contents);
723     va_end (args);
724   }
725   
726   opt |= TAB_JOIN;
727   
728   {
729     struct len_string *cc = &table->cc[x1 + y1 * table->cf];
730     unsigned char *ct = &table->ct[x1 + y1 * table->cf];
731     const int ofs = table->cf - (x2 - x1);
732
733     int y;
734     
735     for (y = y1; y < y2; y++)
736       {
737         int x;
738         
739         for (x = x1; x < x2; x++)
740           {
741             ls_init (cc++, (char *) j, 0);
742             *ct++ = opt;
743           }
744         
745         cc += ofs;
746         ct += ofs;
747       }
748   }
749 }
750
751 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
752 void
753 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
754          struct len_string *string)
755 {
756   assert (table != NULL && string != NULL);
757   
758 #if GLOBAL_DEBUGGING
759   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
760       || c + table->col_ofs >= table->nc
761       || r + table->row_ofs >= table->nr)
762     {
763       printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
764               "(%d,%d)\n",
765               c, table->col_ofs, c + table->col_ofs,
766               r, table->row_ofs, r + table->row_ofs,
767               table->nc, table->nr);
768       return;
769     }
770 #endif
771
772   table->cc[c + r * table->cf] = *string;
773   table->ct[c + r * table->cf] = opt;
774 }
775 \f
776 /* Miscellaneous. */
777
778 /* Sets the widths of all the columns and heights of all the rows in
779    table T for driver D. */
780 static void
781 nowrap_dim (struct tab_table *t, struct outp_driver *d)
782 {
783   t->w[0] = tab_natural_width (t, d, 0);
784   t->h[0] = d->font_height;
785 }
786
787 /* Sets the widths of all the columns and heights of all the rows in
788    table T for driver D. */
789 static void
790 wrap_dim (struct tab_table *t, struct outp_driver *d)
791 {
792   t->w[0] = tab_natural_width (t, d, 0);
793   t->h[0] = tab_natural_height (t, d, 0);
794 }
795
796 /* Outputs text BUF as a table with a single cell having cell options
797    OPTIONS, which is a combination of the TAB_* and TAT_*
798    constants. */
799 void
800 tab_output_text (int options, const char *buf, ...)
801 {
802   struct tab_table *t = tab_create (1, 1, 0);
803
804   assert (buf != NULL);
805   if (options & TAT_PRINTF)
806     {
807       va_list args;
808       char *temp_buf = local_alloc (4096);
809       
810       va_start (args, buf);
811       nvsprintf (temp_buf, buf, args);
812       buf = temp_buf;
813       va_end (args);
814     }
815   
816   if (options & TAT_FIX)
817     {
818       struct outp_driver *d;
819
820       for (d = outp_drivers (NULL); d; d = outp_drivers (d))
821         {
822           if (!d->page_open)
823             d->class->open_page (d);
824
825           if (d->class->text_set_font_by_name != NULL)
826             d->class->text_set_font_by_name (d, "FIXED");
827           else 
828             {
829               /* FIXME */
830             }
831         }
832     }
833
834   tab_text (t, 0, 0, options &~ TAT_PRINTF, buf);
835   tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
836   if (options & TAT_NOWRAP)
837     tab_dim (t, nowrap_dim);
838   else
839     tab_dim (t, wrap_dim);
840   tab_submit (t);
841
842   if (options & TAT_FIX)
843     {
844       struct outp_driver *d;
845
846       for (d = outp_drivers (NULL); d; d = outp_drivers (d))
847         if (d->class->text_set_font_by_name != NULL)
848           d->class->text_set_font_by_name (d, "PROP");
849         else 
850           {
851             /* FIXME */
852           }
853     }
854   
855   if (options & TAT_PRINTF)
856     local_free (buf);
857 }
858
859 /* Set table flags to FLAGS. */
860 void
861 tab_flags (struct tab_table *t, unsigned flags)
862 {
863   assert (t != NULL);
864   t->flags = flags;
865 }
866
867 /* Easy, type-safe way to submit a tab table to som. */
868 void
869 tab_submit (struct tab_table *t)
870 {
871   struct som_entity s;
872
873   assert (t != NULL);
874   s.class = &tab_table_class;
875   s.ext = t;
876   s.type = SOM_TABLE;
877   som_submit (&s);
878   tab_destroy (t);
879 }
880 \f
881 /* Editing. */
882
883 /* Set table row and column offsets for all functions that affect
884    cells or rules. */
885 void
886 tab_offset (struct tab_table *t, int col, int row)
887 {
888   int diff = 0;
889
890   assert (t != NULL);
891 #if GLOBAL_DEBUGGING
892   if (row < -1 || row >= t->nr)
893     {
894       printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
895       abort ();
896     }
897   if (col < -1 || col >= t->nc)
898     {
899       printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
900       abort ();
901     }
902 #endif
903
904   if (row != -1)
905     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
906   if (col != -1)
907     diff += (col - t->col_ofs), t->col_ofs = col;
908
909   t->cc += diff;
910   t->ct += diff;
911 }
912
913 /* Increment the row offset by one. If the table is too small,
914    increase its size. */
915 void
916 tab_next_row (struct tab_table *t)
917 {
918   assert (t != NULL);
919   t->cc += t->cf;
920   t->ct += t->cf;
921   if (++t->row_ofs >= t->nr)
922     tab_realloc (t, -1, t->nr * 4 / 3);
923 }
924 \f
925 static struct tab_table *t;
926 static struct outp_driver *d;
927 int tab_hit;
928
929 /* Set the current table to TABLE. */
930 static void
931 tabi_table (struct som_entity *table)
932 {
933   assert (table != NULL);
934   assert (table->type == SOM_TABLE);
935
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_c_str (&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_c_str (&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