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