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