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