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