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