Fix assertion for proper Huffman merge pattern: 0 == 1 modulo 1.
[pspp] / 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_table s;
872
873   assert (t != NULL);
874   s.class = &tab_table_class;
875   s.ext = t;
876   som_submit (&s);
877   tab_destroy (t);
878 }
879 \f
880 /* Editing. */
881
882 /* Set table row and column offsets for all functions that affect
883    cells or rules. */
884 void
885 tab_offset (struct tab_table *t, int col, int row)
886 {
887   int diff = 0;
888
889   assert (t != NULL);
890 #if GLOBAL_DEBUGGING
891   if (row < -1 || row >= t->nr)
892     {
893       printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
894       abort ();
895     }
896   if (col < -1 || col >= t->nc)
897     {
898       printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
899       abort ();
900     }
901 #endif
902
903   if (row != -1)
904     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
905   if (col != -1)
906     diff += (col - t->col_ofs), t->col_ofs = col;
907
908   t->cc += diff;
909   t->ct += diff;
910 }
911
912 /* Increment the row offset by one. If the table is too small,
913    increase its size. */
914 void
915 tab_next_row (struct tab_table *t)
916 {
917   assert (t != NULL);
918   t->cc += t->cf;
919   t->ct += t->cf;
920   if (++t->row_ofs >= t->nr)
921     tab_realloc (t, -1, t->nr * 4 / 3);
922 }
923 \f
924 static struct tab_table *t;
925 static struct outp_driver *d;
926 int tab_hit;
927
928 /* Set the current table to TABLE. */
929 static void
930 tabi_table (struct som_table *table)
931 {
932   assert (table != NULL);
933   t = table->ext;
934   tab_offset (t, 0, 0);
935   
936   assert (t->w == NULL && t->h == NULL);
937   t->w = pool_alloc (t->container, sizeof *t->w * t->nc);
938   t->h = pool_alloc (t->container, sizeof *t->h * t->nr);
939 }
940
941 /* Set the current output device to DRIVER. */
942 static void
943 tabi_driver (struct outp_driver *driver)
944 {
945   int i;
946
947   assert (driver != NULL);
948   d = driver;
949   
950   /* Figure out sizes of rules. */
951   for (t->hr_tot = i = 0; i <= t->nr; i++)
952     t->hr_tot += t->hrh[i] = d->horiz_line_spacing[t->trh[i]];
953   for (t->vr_tot = i = 0; i <= t->nc; i++)
954     t->vr_tot += t->wrv[i] = d->vert_line_spacing[t->trv[i]];
955
956 #if GLOBAL_DEBUGGING
957   for (i = 0; i < t->nr; i++)
958     t->h[i] = -1;
959   for (i = 0; i < t->nc; i++)
960     t->w[i] = -1;
961 #endif
962
963   assert (t->dim != NULL);
964   t->dim (t, d);
965
966 #if GLOBAL_DEBUGGING
967   {
968     int error = 0;
969
970     for (i = 0; i < t->nr; i++)
971       {
972         if (t->h[i] == -1)
973           {
974             printf ("Table row %d height not initialized.\n", i);
975             error = 1;
976           }
977         assert (t->h[i] > 0);
978       }
979     
980     for (i = 0; i < t->nc; i++)
981       {
982         if (t->w[i] == -1)
983           {
984             printf ("Table column %d width not initialized.\n", i);
985             error = 1;
986           }
987         assert (t->w[i] > 0);
988       }
989   }
990 #endif
991     
992   /* Add up header sizes. */
993   for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
994     t->wl += t->w[i] + t->wrv[i + 1];
995   for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
996     t->ht += t->h[i] + t->hrh[i + 1];
997   for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
998     t->wr += t->w[i] + t->wrv[i + 1];
999   for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
1000     t->hb += t->h[i] + t->hrh[i + 1];
1001   
1002   /* Title. */
1003   if (!(t->flags & SOMF_NO_TITLE))
1004     t->ht += d->font_height;
1005 }
1006
1007 /* Return the number of columns and rows in the table into N_COLUMNS
1008    and N_ROWS, respectively. */
1009 static void
1010 tabi_count (int *n_columns, int *n_rows)
1011 {
1012   assert (n_columns != NULL && n_rows != NULL);
1013   *n_columns = t->nc;
1014   *n_rows = t->nr;
1015 }
1016
1017 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
1018
1019 /* Return the horizontal and vertical size of the entire table,
1020    including headers, for the current output device, into HORIZ and
1021    VERT. */
1022 static void
1023 tabi_area (int *horiz, int *vert)
1024 {
1025   assert (horiz != NULL && vert != NULL);
1026   
1027   {
1028     int w, c;
1029     
1030     for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
1031          c < t->nc - t->r; c++)
1032       w += t->w[c] + t->wrv[c];
1033     *horiz = w;
1034   }
1035   
1036   {
1037     int h, r;
1038     for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1039          r < t->nr - t->b; r++)
1040       h += t->h[r] + t->hrh[r];
1041     *vert = h;
1042   }
1043 }
1044
1045 /* Return the column style for this table into STYLE. */
1046 static void
1047 tabi_columns (int *style)
1048 {
1049   assert (style != NULL);
1050   *style = t->col_style;
1051 }
1052
1053 /* Return the number of header rows/columns on the left, right, top,
1054    and bottom sides into HL, HR, HT, and HB, respectively. */
1055 static void
1056 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1057 {
1058   assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1059   *hl = t->l;
1060   *hr = t->r;
1061   *ht = t->t;
1062   *hb = t->b;
1063 }
1064
1065 /* Determines the number of rows or columns (including appropriate
1066    headers), depending on CUMTYPE, that will fit into the space
1067    specified.  Takes rows/columns starting at index START and attempts
1068    to fill up available space MAX.  Returns in END the index of the
1069    last row/column plus one; returns in ACTUAL the actual amount of
1070    space the selected rows/columns (including appropriate headers)
1071    filled. */
1072 static void
1073 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1074 {
1075   int n;
1076   int *d;
1077   int *r;
1078   int total;
1079   
1080   assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1081   if (cumtype == SOM_ROWS)
1082     {
1083       assert (start >= 0 && start < t->nr);
1084       n = t->nr - t->b;
1085       d = &t->h[start];
1086       r = &t->hrh[start + 1];
1087       total = t->ht + t->hb;
1088     } else {
1089       assert (start >= 0 && start < t->nc);
1090       n = t->nc - t->r;
1091       d = &t->w[start];
1092       r = &t->wrv[start + 1];
1093       total = t->wl + t->wr;
1094     }
1095   
1096   total += *d++;
1097   if (total > max)
1098     {
1099       if (end)
1100         *end = start;
1101       if (actual)
1102         *actual = 0;
1103       return;
1104     }
1105     
1106   {
1107     int x;
1108       
1109     for (x = start + 1; x < n; x++)
1110       {
1111         int amt = *d++ + *r++;
1112         
1113         total += amt;
1114         if (total > max)
1115           {
1116             total -= amt;
1117             break;
1118           }
1119       }
1120
1121     if (end)
1122       *end = x;
1123     
1124     if (actual)
1125       *actual = total;
1126   }
1127 }
1128
1129 /* Return flags set for the current table into FLAGS. */
1130 static void
1131 tabi_flags (unsigned *flags)
1132 {
1133   assert (flags != NULL);
1134   *flags = t->flags;
1135 }
1136
1137 /* Render title for current table, with major index X and minor index
1138    Y.  Y may be zero, or X and Y may be zero, but X should be nonzero
1139    if Y is nonzero. */
1140 static void
1141 tabi_title (int x, int y)
1142 {
1143   char buf[1024];
1144   char *cp;
1145
1146   if (t->flags & SOMF_NO_TITLE)
1147     return;
1148   
1149   cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1150   if (x && y)
1151     cp = spprintf (cp, "(%d:%d)", x, y);
1152   else if (x)
1153     cp = spprintf (cp, "(%d)", x);
1154   if (cur_proc)
1155     cp = spprintf (cp, " %s", cur_proc);
1156   cp = stpcpy (cp, ".  ");
1157   if (!ls_empty_p (&t->title))
1158     {
1159       memcpy (cp, ls_c_str (&t->title), ls_length (&t->title));
1160       cp += ls_length (&t->title);
1161     }
1162   *cp = 0;
1163   
1164   {
1165     struct outp_text text;
1166
1167     text.options = OUTP_T_JUST_LEFT | OUTP_T_HORZ | OUTP_T_VERT;
1168     ls_init (&text.s, buf, cp - buf);
1169     text.h = d->width;
1170     text.v = d->font_height;
1171     text.x = 0;
1172     text.y = d->cp_y;
1173     d->class->text_draw (d, &text);
1174   }
1175 }
1176
1177 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1178
1179 /* Draws the table region in rectangle (X1,Y1)-(X2,Y2), where column
1180    X2 and row Y2 are not included in the rectangle, at the current
1181    position on the current output device.  Draws headers as well. */
1182 static void
1183 tabi_render (int x1, int y1, int x2, int y2)
1184 {
1185   int i, y;
1186   int ranges[3][2];
1187   
1188   tab_hit++;
1189
1190   y = d->cp_y;
1191   if (!(t->flags & SOMF_NO_TITLE))
1192     y += d->font_height;
1193
1194   /* Top headers. */
1195   ranges[0][0] = 0;
1196   ranges[0][1] = t->t * 2 + 1;
1197
1198   /* Requested rows. */
1199   ranges[1][0] = y1 * 2 + 1;
1200   ranges[1][1] = y2 * 2;
1201
1202   /* Bottom headers. */
1203   ranges[2][0] = (t->nr - t->b) * 2;
1204   ranges[2][1] = t->nr * 2 + 1;
1205
1206   for (i = 0; i < 3; i++) 
1207     {
1208       int r;
1209
1210       for (r = ranges[i][0]; r < ranges[i][1]; r++) 
1211         {
1212           int x = d->cp_x;
1213           x += render_strip (x, y, r, 0, t->l * 2 + 1, y1, y2);
1214           x += render_strip (x, y, r, x1 * 2 + 1, x2 * 2, y1, y2);
1215           x += render_strip (x, y, r, (t->nc - t->r) * 2,
1216                              t->nc * 2 + 1, y1, y2);
1217           y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2]; 
1218         }
1219     }
1220 }
1221
1222 struct som_table_class tab_table_class =
1223   {
1224     tabi_table,
1225     tabi_driver,
1226     
1227     tabi_count,
1228     tabi_area,
1229     NULL,
1230     NULL,
1231     tabi_columns,
1232     NULL,
1233     tabi_headers,
1234     NULL,
1235     tabi_cumulate,
1236     tabi_flags,
1237     
1238     NULL,
1239     NULL,
1240
1241     tabi_title,
1242     tabi_render,
1243   };
1244 \f
1245 /* Render contiguous strip consisting of columns C1...C2, exclusive,
1246    on row R, at location (X,Y).  Return width of the strip thus
1247    rendered.
1248
1249    Renders joined cells, even those outside the strip, within the
1250    rendering region (C1,R1)-(C2,R2).
1251
1252    For the purposes of counting rows and columns in this function
1253    only, horizontal rules are considered rows and vertical rules are
1254    considered columns.
1255
1256    FIXME: Doesn't use r1?  Huh?  */
1257 static int
1258 render_strip (int x, int y, int r, int c1, int c2, int r1 UNUSED, int r2)
1259 {
1260   int x_origin = x;
1261
1262   /* Horizontal rules. */
1263   if ((r & 1) == 0)
1264     {
1265       int hrh = t->hrh[r / 2];
1266       int c;
1267
1268       for (c = c1; c < c2; c++)
1269         {
1270           if (c & 1)
1271             {
1272               int style = t->rh[(c / 2) + (r / 2 * t->cf)];
1273
1274               if (style != TAL_0)
1275                 {
1276                   const struct color clr = {0, 0, 0, 0};
1277                   struct rect rct;
1278
1279                   rct.x1 = x;
1280                   rct.y1 = y;
1281                   rct.x2 = x + t->w[c / 2];
1282                   rct.y2 = y + hrh;
1283                   d->class->line_horz (d, &rct, &clr, style);
1284                 }
1285               x += t->w[c / 2];
1286             } else {
1287               const struct color clr = {0, 0, 0, 0};
1288               struct rect rct;
1289               struct outp_styles s;
1290
1291               rct.x1 = x;
1292               rct.y1 = y;
1293               rct.x2 = x + t->wrv[c / 2];
1294               rct.y2 = y + hrh;
1295
1296               s.t = r > 0 ? t->rv[(c / 2) + (t->cf + 1) * (r / 2 - 1)] : 0;
1297               s.b = r < 2 * t->nr ? t->rv[(c / 2) + (t->cf + 1) * (r / 2)] : 0;
1298               s.l = c > 0 ? t->rh[(c / 2 - 1) + t->cf * (r / 2)] : 0;
1299               s.r = c < 2 * t->nc ? t->rh[(c / 2) + t->cf * (r / 2)] : 0;
1300
1301               if (s.t | s.b | s.l | s.r)
1302                 d->class->line_intersection (d, &rct, &clr, &s);
1303               
1304               x += t->wrv[c / 2];
1305             }
1306         }
1307     } else {
1308       int c;
1309
1310       for (c = c1; c < c2; c++)
1311         {
1312           if (c & 1)
1313             {
1314               const int index = (c / 2) + (r / 2 * t->cf);
1315
1316               if (!(t->ct[index] & TAB_JOIN))
1317                 {
1318                   struct outp_text text;
1319
1320                   text.options = ((t->ct[index] & OUTP_T_JUST_MASK)
1321                                   | OUTP_T_HORZ | OUTP_T_VERT);
1322                   if ((t->ct[index] & TAB_EMPTY) == 0)
1323                     {
1324                       text.s = t->cc[index];
1325                       assert (!ls_null_p (&text.s));
1326                       text.h = t->w[c / 2];
1327                       text.v = t->h[r / 2];
1328                       text.x = x;
1329                       text.y = y;
1330                       d->class->text_draw (d, &text);
1331                     }
1332                 } else {
1333                   struct tab_joined_cell *j =
1334                     (struct tab_joined_cell *) ls_c_str (&t->cc[index]);
1335
1336                   if (j->hit != tab_hit)
1337                     {
1338                       j->hit = tab_hit;
1339
1340                       if (j->x1 == c / 2 && j->y1 == r / 2)
1341                         {
1342                           struct outp_text text;
1343
1344                           text.options = ((t->ct[index] & OUTP_T_JUST_MASK)
1345                                           | OUTP_T_HORZ | OUTP_T_VERT);
1346                           text.s = j->contents;
1347                           text.x = x;
1348                           text.y = y;
1349                           
1350                           {
1351                             int c;
1352
1353                             for (c = j->x1, text.h = -t->wrv[j->x2];
1354                                  c < j->x2 && c < c2 / 2; c++) 
1355                                 text.h += t->w[c] + t->wrv[c + 1]; 
1356                           }
1357                           
1358                           {
1359                             int r;
1360
1361                             for (r = j->y1, text.v = -t->hrh[j->y2];
1362                                  r < j->y2 && r < r2 / 2; r++)
1363                               text.v += t->h[r] + t->hrh[r + 1];
1364                           }
1365                           d->class->text_draw (d, &text);
1366                         }
1367                     }
1368                 }
1369               x += t->w[c / 2];
1370             } else {
1371               int style = t->rv[(c / 2) + (r / 2 * (t->cf + 1))];
1372
1373               if (style != TAL_0)
1374                 {
1375                   const struct color clr = {0, 0, 0, 0};
1376                   struct rect rct;
1377
1378                   rct.x1 = x;
1379                   rct.y1 = y;
1380                   rct.x2 = x + t->wrv[c / 2];
1381                   rct.y2 = y + t->h[r / 2];
1382                   d->class->line_vert (d, &rct, &clr, style);
1383                 }
1384               x += t->wrv[c / 2];
1385             }
1386         }
1387     }
1388
1389   return x - x_origin;
1390 }
1391