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