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