Clean up output subsystem.
[pspp-builds.git] / src / output / manager.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include "manager.h"
22 #include <libpspp/message.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include "output.h"
26 #include <libpspp/debug-print.h>
27
28 /* Table. */
29 int table_num = 1;
30 int subtable_num;
31 \f
32 /* Increments table_num so different procedures' output can be
33    distinguished. */
34 void
35 som_new_series (void)
36 {
37   if (subtable_num != 0)
38     {
39       table_num++;
40       subtable_num = 0;
41     }
42 }
43
44 /* Ejects the paper for all active devices. */
45 void
46 som_eject_page (void)
47 {
48   struct outp_driver *d;
49
50   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
51     outp_eject_page (d);
52 }
53
54 /* Skip down a single line on all active devices. */
55 void
56 som_blank_line (void)
57 {
58   struct outp_driver *d;
59   
60   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
61     if (d->page_open && d->cp_y != 0)
62       d->cp_y += d->font_height;
63 }
64 \f
65 /* Driver. */
66 static struct outp_driver *d=0;
67
68 /* Table. */
69 static struct som_entity *t=0;
70
71 /* Flags. */
72 static unsigned flags;
73
74 /* Number of columns, rows. */
75 static int nc, nr;
76
77 /* Number of columns or rows in left, right, top, bottom headers. */
78 static int hl, hr, ht, hb;
79
80 /* Column style. */
81 static int cs;
82
83 /* Table height, width. */
84 static int th, tw;
85
86 static void render_columns (void);
87 static void render_simple (void);
88 static void render_segments (void);
89
90 static void output_entity (struct outp_driver *, struct som_entity *);
91
92 /* Output table T to appropriate output devices. */
93 void
94 som_submit (struct som_entity *t)
95 {
96 #if DEBUGGING
97   static int entry;
98   
99   assert (entry++ == 0);
100 #endif
101
102   if ( t->type == SOM_TABLE) 
103     {
104       t->class->table (t);
105       t->class->flags (&flags);
106       t->class->count (&nc, &nr);
107       t->class->headers (&hl, &hr, &ht, &hb);
108
109
110 #if DEBUGGING
111       if (hl + hr > nc || ht + hb > nr)
112         {
113           printf ("headers: (l,r)=(%d,%d), (t,b)=(%d,%d) in table size (%d,%d)\n",
114                   hl, hr, ht, hb, nc, nr);
115           abort ();
116         }
117       else if (hl + hr == nc)
118         printf ("warning: headers (l,r)=(%d,%d) in table width %d\n", hl, hr, nc);
119       else if (ht + hb == nr)
120         printf ("warning: headers (t,b)=(%d,%d) in table height %d\n", ht, hb, nr);
121 #endif
122
123       t->class->columns (&cs);
124
125       if (!(flags & SOMF_NO_TITLE))
126         subtable_num++;
127   
128     }
129   
130   {
131     struct outp_driver *d;
132     
133     for (d = outp_drivers (NULL); d; d = outp_drivers (d))
134         output_entity (d, t);
135
136   }
137   
138 #if DEBUGGING
139   assert (--entry == 0);
140 #endif
141 }
142
143 /* Output entity ENTITY to driver DRIVER. */
144 static void
145 output_entity (struct outp_driver *driver, struct som_entity *entity)
146 {
147   bool fits_width, fits_length;
148   d = driver;
149
150   outp_open_page (d);
151   if (d->class->special || entity->type == SOM_CHART)
152     {
153       driver->class->submit (d, entity);
154       return;
155     }
156
157   t = entity;
158   
159   t->class->driver (d);
160   t->class->area (&tw, &th);
161   fits_width = t->class->fits_width (d->width);
162   fits_length = t->class->fits_length (d->length);
163   if (!fits_width || !fits_length) 
164     {
165       int tl, tr, tt, tb;
166       tl = fits_width ? hl : 0;
167       tr = fits_width ? hr : 0;
168       tt = fits_length ? ht : 0;
169       tb = fits_length ? hb : 0;
170       t->class->set_headers (tl, tr, tt, tb);
171       t->class->driver (d);
172       t->class->area (&tw, &th);
173     }
174   
175   if (!(flags & SOMF_NO_SPACING) && d->cp_y != 0)
176     d->cp_y += d->font_height;
177         
178   if (cs != SOM_COL_NONE
179       && 2 * (tw + d->prop_em_width) <= d->width
180       && nr - (ht + hb) > 5)
181     render_columns ();
182   else if (tw < d->width && th + d->cp_y < d->length)
183     render_simple ();
184   else 
185     render_segments ();
186
187   t->class->set_headers (hl, hr, ht, hb);
188 }
189
190 /* Render the table into multiple columns. */
191 static void
192 render_columns (void)
193 {
194   int y0, y1;
195   int max_len = 0;
196   int index = 0;
197   
198   assert (cs == SOM_COL_DOWN);
199   assert (d->cp_x == 0);
200
201   for (y0 = ht; y0 < nr - hb; y0 = y1)
202     {
203       int len;
204       
205       t->class->cumulate (SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
206
207       if (y0 == y1)
208         {
209           assert (d->cp_y);
210           outp_eject_page (d);
211         } else {
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             } else {
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 }