Fix missing @clicksequence problem with older Texinfo versions.
[pspp-builds.git] / src / output / manager.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2007 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 #include "manager.h"
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <libpspp/assertion.h>
22 #include "output.h"
23
24 /* Table. */
25 int table_num = 1;
26 int subtable_num;
27 \f
28 /* Increments table_num so different procedures' output can be
29    distinguished. */
30 void
31 som_new_series (void)
32 {
33   if (subtable_num != 0)
34     {
35       table_num++;
36       subtable_num = 0;
37     }
38 }
39
40 /* Ejects the paper for all active devices. */
41 void
42 som_eject_page (void)
43 {
44   struct outp_driver *d;
45
46   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
47     outp_eject_page (d);
48 }
49
50 /* Flushes output on all active devices. */
51 void
52 som_flush (void)
53 {
54   struct outp_driver *d;
55
56   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
57     outp_flush (d);
58 }
59
60 /* Skip down a single line on all active devices. */
61 void
62 som_blank_line (void)
63 {
64   struct outp_driver *d;
65
66   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
67     if (d->page_open && d->cp_y != 0)
68       d->cp_y += d->font_height;
69 }
70 \f
71 /* Driver. */
72 static struct outp_driver *d = 0;
73
74 /* Table. */
75 static struct som_entity *t = 0;
76
77 /* Flags. */
78 static unsigned flags;
79
80 /* Number of columns, rows. */
81 static int nc, nr;
82
83 /* Number of columns or rows in left, right, top, bottom headers. */
84 static int hl, hr, ht, hb;
85
86 /* Column style. */
87 static int cs;
88
89 /* Table height, width. */
90 static int th, tw;
91
92 static void render_columns (void);
93 static void render_simple (void);
94 static void render_segments (void);
95
96 static void output_entity (struct outp_driver *, struct som_entity *);
97
98 /* Output table T to appropriate output devices. */
99 void
100 som_submit (struct som_entity *t)
101 {
102 #if DEBUGGING
103   static int entry;
104
105   assert (entry++ == 0);
106 #endif
107
108   if ( t->type == SOM_TABLE)
109     {
110       t->class->table (t);
111       t->class->flags (&flags);
112       t->class->count (&nc, &nr);
113       t->class->headers (&hl, &hr, &ht, &hb);
114
115
116 #if DEBUGGING
117       if (hl + hr > nc || ht + hb > nr)
118         {
119           printf ("headers: (l,r)=(%d,%d), (t,b)=(%d,%d) in table size (%d,%d)\n",
120                   hl, hr, ht, hb, nc, nr);
121           NOT_REACHED ();
122         }
123       else if (hl + hr == nc)
124         printf ("warning: headers (l,r)=(%d,%d) in table width %d\n", hl, hr, nc);
125       else if (ht + hb == nr)
126         printf ("warning: headers (t,b)=(%d,%d) in table height %d\n", ht, hb, nr);
127 #endif
128
129       t->class->columns (&cs);
130
131       if (!(flags & SOMF_NO_TITLE))
132         subtable_num++;
133
134     }
135
136   {
137     struct outp_driver *d;
138
139     for (d = outp_drivers (NULL); d; d = outp_drivers (d))
140         output_entity (d, t);
141
142   }
143
144 #if DEBUGGING
145   assert (--entry == 0);
146 #endif
147 }
148
149 /* Output entity ENTITY to driver DRIVER. */
150 static void
151 output_entity (struct outp_driver *driver, struct som_entity *entity)
152 {
153   bool fits_width, fits_length;
154   d = driver;
155
156   outp_open_page (d);
157   if (d->class->special || entity->type == SOM_CHART)
158     {
159       driver->class->submit (d, entity);
160       return;
161     }
162
163   t = entity;
164
165   t->class->driver (d);
166   t->class->area (&tw, &th);
167   fits_width = t->class->fits_width (d->width);
168   fits_length = t->class->fits_length (d->length);
169   if (!fits_width || !fits_length)
170     {
171       int tl, tr, tt, tb;
172       tl = fits_width ? hl : 0;
173       tr = fits_width ? hr : 0;
174       tt = fits_length ? ht : 0;
175       tb = fits_length ? hb : 0;
176       t->class->set_headers (tl, tr, tt, tb);
177       t->class->driver (d);
178       t->class->area (&tw, &th);
179     }
180
181   if (!(flags & SOMF_NO_SPACING) && d->cp_y != 0)
182     d->cp_y += d->font_height;
183
184   if (cs != SOM_COL_NONE
185       && 2 * (tw + d->prop_em_width) <= d->width
186       && nr - (ht + hb) > 5)
187     render_columns ();
188   else if (tw < d->width && th + d->cp_y < d->length)
189     render_simple ();
190   else
191     render_segments ();
192
193   t->class->set_headers (hl, hr, ht, hb);
194 }
195
196 /* Render the table into multiple columns. */
197 static void
198 render_columns (void)
199 {
200   int y0, y1;
201   int max_len = 0;
202   int index = 0;
203
204   assert (cs == SOM_COL_DOWN);
205   assert (d->cp_x == 0);
206
207   for (y0 = ht; y0 < nr - hb; y0 = y1)
208     {
209       int len;
210
211       t->class->cumulate (SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
212
213       if (y0 == y1)
214         {
215           assert (d->cp_y);
216           outp_eject_page (d);
217         }
218       else
219         {
220           if (len > max_len)
221             max_len = len;
222
223           t->class->title (index++, 0);
224           t->class->render (0, y0, nc, y1);
225
226           d->cp_x += tw + 2 * d->prop_em_width;
227           if (d->cp_x + tw > d->width)
228             {
229               d->cp_x = 0;
230               d->cp_y += max_len;
231               max_len = 0;
232             }
233         }
234     }
235
236   if (d->cp_x > 0)
237     {
238       d->cp_x = 0;
239       d->cp_y += max_len;
240     }
241 }
242
243 /* Render the table by itself on the current page. */
244 static void
245 render_simple (void)
246 {
247   assert (d->cp_x == 0);
248   assert (tw < d->width && th + d->cp_y < d->length);
249
250   t->class->title (0, 0);
251   t->class->render (hl, ht, nc - hr, nr - hb);
252   d->cp_y += th;
253 }
254
255 /* General table breaking routine. */
256 static void
257 render_segments (void)
258 {
259   int count = 0;
260
261   int x_index;
262   int x0, x1;
263
264   assert (d->cp_x == 0);
265
266   for (x_index = 0, x0 = hl; x0 < nc - hr; x0 = x1, x_index++)
267     {
268       int y_index;
269       int y0, y1;
270
271       t->class->cumulate (SOM_COLUMNS, x0, &x1, d->width, NULL);
272       if (x_index == 0 && x1 != nc - hr)
273         x_index++;
274
275       for (y_index = 0, y0 = ht; y0 < nr - hb; y0 = y1, y_index++)
276         {
277           int len;
278
279           if (count++ != 0 && d->cp_y != 0)
280             d->cp_y += d->font_height;
281
282           t->class->cumulate (SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
283           if (y_index == 0 && y1 != nr - hb)
284             y_index++;
285
286           if (y0 == y1)
287             {
288               assert (d->cp_y);
289               outp_eject_page (d);
290             }
291           else
292             {
293               t->class->title (x_index ? x_index : y_index,
294                                x_index ? y_index : 0);
295               t->class->render (x0, y0, x1, y1);
296
297               d->cp_y += len;
298             }
299         }
300     }
301 }