Get rid of most global variables in outputting tables.
[pspp-builds.git] / src / output / manager.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2007, 2009 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 static void render_columns (void *r, struct outp_driver *, struct som_entity *,
72                             int tw, int th,
73                             int hl, int hr, int ht, int hb);
74 static void render_simple (void *r, struct outp_driver *, struct som_entity *,
75                            int tw, int th,
76                            int hl, int hr, int ht, int hb);
77 static void render_segments (void *r, struct outp_driver *,
78                              struct som_entity *,
79                              int tw, int th,
80                              int hl, int hr, int ht, int hb);
81
82 static void output_entity (struct outp_driver *, struct som_entity *);
83
84 /* Output table T to appropriate output devices. */
85 void
86 som_submit (struct som_entity *t)
87 {
88   struct outp_driver *d;
89
90 #if DEBUGGING
91   static int entry;
92
93   assert (entry++ == 0);
94 #endif
95
96   if (t->type == SOM_TABLE)
97     {
98       unsigned int flags;
99       int hl, hr, ht, hb;
100       int nc, nr;
101
102       /* Set up to render the table. */
103       t->class->flags (t, &flags);
104       if (!(flags & SOMF_NO_TITLE))
105         subtable_num++;
106
107       /* Do some basic error checking. */
108       t->class->count (t, &nc, &nr);
109       t->class->headers (t, &hl, &hr, &ht, &hb);
110       if (hl + hr > nc || ht + hb > nr)
111         {
112           fprintf (stderr, "headers: (l,r)=(%d,%d), (t,b)=(%d,%d) "
113                    "in table size (%d,%d)\n",
114                    hl, hr, ht, hb, nc, nr);
115           NOT_REACHED ();
116         }
117       else if (hl + hr == nc)
118         fprintf (stderr, "warning: headers (l,r)=(%d,%d) in table width %d\n",
119                 hl, hr, nc);
120       else if (ht + hb == nr)
121         fprintf (stderr, "warning: headers (t,b)=(%d,%d) in table height %d\n",
122                 ht, hb, nr);
123     }
124
125   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
126     output_entity (d, t);
127
128 #if DEBUGGING
129   assert (--entry == 0);
130 #endif
131 }
132
133 static bool
134 check_fits_width (struct som_entity *t, const struct outp_driver *d, void *r)
135 {
136   int hl, hr, ht, hb;
137   int nc, nr;
138   int i;
139
140   t->class->headers (t, &hl, &hr, &ht, &hb);
141   t->class->count (t, &nc, &nr);
142   for (i = hl; i < nc - hr; i++)
143     {
144       int end, actual;
145       t->class->cumulate (r, SOM_COLUMNS, i, &end, d->width, &actual);
146       if (end == i)
147         return false;
148     }
149
150   return true;
151 }
152
153 static bool
154 check_fits_length (struct som_entity *t, const struct outp_driver *d, void *r)
155 {
156   int hl, hr, ht, hb;
157   int nc, nr;
158   int i;
159
160   t->class->headers (t, &hl, &hr, &ht, &hb);
161   t->class->count (t, &nc, &nr);
162   for (i = ht; i < nr - hb; i++)
163     {
164       int end, actual;
165       t->class->cumulate (r, SOM_ROWS, i, &end, d->length, &actual);
166       if (end == i)
167         return false;
168     }
169
170   return true;
171 }
172
173 /* Output entity T to driver D. */
174 static void
175 output_entity (struct outp_driver *d, struct som_entity *t)
176 {
177   bool fits_width, fits_length;
178   unsigned int flags;
179   int hl, hr, ht, hb;
180   int tw, th;
181   int nc, nr;
182   int cs;
183   void *r;
184
185   outp_open_page (d);
186   if (d->class->special || t->type == SOM_CHART)
187     {
188       d->class->submit (d, t);
189       return;
190     }
191
192   t->class->headers (t, &hl, &hr, &ht, &hb);
193   t->class->count (t, &nc, &nr);
194   t->class->columns (t, &cs);
195   t->class->flags (t, &flags);
196
197   r = t->class->render_init (t, d, hl, hr, ht, hb);
198
199   fits_width = check_fits_width (t, d, r);
200   fits_length = check_fits_length (t, d, r);
201   if (!fits_width || !fits_length)
202     {
203       t->class->render_free (r);
204
205       if (!fits_width)
206         hl = hr = 0;
207       if (!fits_length)
208         ht = hb = 0;
209
210       r = t->class->render_init (t, d, hl, hr, ht, hb);
211     }
212   t->class->area (r, &tw, &th);
213
214   if (!(flags & SOMF_NO_SPACING) && d->cp_y != 0)
215     d->cp_y += d->font_height;
216
217   if (cs != SOM_COL_NONE
218       && 2 * (tw + d->prop_em_width) <= d->width
219       && nr - (ht + hb) > 5)
220     render_columns (r, d, t, tw, th, hl, hr, ht, hb);
221   else if (tw < d->width && th + d->cp_y < d->length)
222     render_simple (r, d, t, tw, th, hl, hr, ht, hb);
223   else
224     render_segments (r, d, t, tw, th, hl, hr, ht, hb);
225
226   t->class->render_free (r);
227 }
228
229 /* Render the table into multiple columns. */
230 static void
231 render_columns (void *r, struct outp_driver *d, struct som_entity *t,
232                 int tw, int th UNUSED,
233                 int hl UNUSED, int hr UNUSED, int ht, int hb)
234 {
235   int y0, y1;
236   int max_len = 0;
237   int index = 0;
238   int nc, nr;
239   int cs;
240
241   t->class->count (t, &nc, &nr);
242   t->class->columns (t, &cs);
243
244   assert (cs == SOM_COL_DOWN);
245   assert (d->cp_x == 0);
246
247   for (y0 = ht; y0 < nr - hb; y0 = y1)
248     {
249       int len;
250
251       t->class->cumulate (r, SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
252
253       if (y0 == y1)
254         {
255           assert (d->cp_y);
256           outp_eject_page (d);
257         }
258       else
259         {
260           if (len > max_len)
261             max_len = len;
262
263           t->class->title (r, index++, 0);
264           t->class->render (r, 0, y0, nc, y1);
265
266           d->cp_x += tw + 2 * d->prop_em_width;
267           if (d->cp_x + tw > d->width)
268             {
269               d->cp_x = 0;
270               d->cp_y += max_len;
271               max_len = 0;
272             }
273         }
274     }
275
276   if (d->cp_x > 0)
277     {
278       d->cp_x = 0;
279       d->cp_y += max_len;
280     }
281 }
282
283 /* Render the table by itself on the current page. */
284 static void
285 render_simple (void *r, struct outp_driver *d, struct som_entity *t,
286                int tw, int th,
287                int hl, int hr, int ht, int hb)
288 {
289   int nc, nr;
290
291   t->class->count (t, &nc, &nr);
292
293   assert (d->cp_x == 0);
294   assert (tw < d->width && th + d->cp_y < d->length);
295
296   t->class->title (r, 0, 0);
297   t->class->render (r, hl, ht, nc - hr, nr - hb);
298   d->cp_y += th;
299 }
300
301 /* General table breaking routine. */
302 static void
303 render_segments (void *r, struct outp_driver *d, struct som_entity *t,
304                  int tw UNUSED, int th UNUSED,
305                  int hl, int hr, int ht, int hb)
306 {
307   int count = 0;
308
309   int x_index;
310   int x0, x1;
311
312   int nc, nr;
313
314   assert (d->cp_x == 0);
315
316   t->class->count (t, &nc, &nr);
317   for (x_index = 0, x0 = hl; x0 < nc - hr; x0 = x1, x_index++)
318     {
319       int y_index;
320       int y0, y1;
321
322       t->class->cumulate (r, SOM_COLUMNS, x0, &x1, d->width, NULL);
323       if (x_index == 0 && x1 != nc - hr)
324         x_index++;
325
326       for (y_index = 0, y0 = ht; y0 < nr - hb; y0 = y1, y_index++)
327         {
328           int len;
329
330           if (count++ != 0 && d->cp_y != 0)
331             d->cp_y += d->font_height;
332
333           t->class->cumulate (r, SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
334           if (y_index == 0 && y1 != nr - hb)
335             y_index++;
336
337           if (y0 == y1)
338             {
339               assert (d->cp_y);
340               outp_eject_page (d);
341             }
342           else
343             {
344               t->class->title (r, x_index ? x_index : y_index,
345                                x_index ? y_index : 0);
346               t->class->render (r, x0, y0, x1, y1);
347
348               d->cp_y += len;
349             }
350         }
351     }
352 }