0d82867ae0f08b83f9448e570de3673d0933ab67
[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 <stdio.h>
23 #include <stdlib.h>
24 #include <libpspp/assertion.h>
25 #include "output.h"
26
27 /* Table. */
28 int table_num = 1;
29 int subtable_num;
30 \f
31 /* Increments table_num so different procedures' output can be
32    distinguished. */
33 void
34 som_new_series (void)
35 {
36   if (subtable_num != 0)
37     {
38       table_num++;
39       subtable_num = 0;
40     }
41 }
42
43 /* Ejects the paper for all active devices. */
44 void
45 som_eject_page (void)
46 {
47   struct outp_driver *d;
48
49   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
50     outp_eject_page (d);
51 }
52
53 /* Skip down a single line on all active devices. */
54 void
55 som_blank_line (void)
56 {
57   struct outp_driver *d;
58   
59   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
60     if (d->page_open && d->cp_y != 0)
61       d->cp_y += d->font_height;
62 }
63 \f
64 /* Driver. */
65 static struct outp_driver *d = 0;
66
67 /* Table. */
68 static struct som_entity *t = 0;
69
70 /* Flags. */
71 static unsigned flags;
72
73 /* Number of columns, rows. */
74 static int nc, nr;
75
76 /* Number of columns or rows in left, right, top, bottom headers. */
77 static int hl, hr, ht, hb;
78
79 /* Column style. */
80 static int cs;
81
82 /* Table height, width. */
83 static int th, tw;
84
85 static void render_columns (void);
86 static void render_simple (void);
87 static void render_segments (void);
88
89 static void output_entity (struct outp_driver *, struct som_entity *);
90
91 /* Output table T to appropriate output devices. */
92 void
93 som_submit (struct som_entity *t)
94 {
95 #if DEBUGGING
96   static int entry;
97   
98   assert (entry++ == 0);
99 #endif
100
101   if ( t->type == SOM_TABLE) 
102     {
103       t->class->table (t);
104       t->class->flags (&flags);
105       t->class->count (&nc, &nr);
106       t->class->headers (&hl, &hr, &ht, &hb);
107
108
109 #if DEBUGGING
110       if (hl + hr > nc || ht + hb > nr)
111         {
112           printf ("headers: (l,r)=(%d,%d), (t,b)=(%d,%d) in table size (%d,%d)\n",
113                   hl, hr, ht, hb, nc, nr);
114           NOT_REACHED ();
115         }
116       else if (hl + hr == nc)
117         printf ("warning: headers (l,r)=(%d,%d) in table width %d\n", hl, hr, nc);
118       else if (ht + hb == nr)
119         printf ("warning: headers (t,b)=(%d,%d) in table height %d\n", ht, hb, nr);
120 #endif
121
122       t->class->columns (&cs);
123
124       if (!(flags & SOMF_NO_TITLE))
125         subtable_num++;
126   
127     }
128   
129   {
130     struct outp_driver *d;
131     
132     for (d = outp_drivers (NULL); d; d = outp_drivers (d))
133         output_entity (d, t);
134
135   }
136   
137 #if DEBUGGING
138   assert (--entry == 0);
139 #endif
140 }
141
142 /* Output entity ENTITY to driver DRIVER. */
143 static void
144 output_entity (struct outp_driver *driver, struct som_entity *entity)
145 {
146   bool fits_width, fits_length;
147   d = driver;
148
149   outp_open_page (d);
150   if (d->class->special || entity->type == SOM_CHART)
151     {
152       driver->class->submit (d, entity);
153       return;
154     }
155
156   t = entity;
157   
158   t->class->driver (d);
159   t->class->area (&tw, &th);
160   fits_width = t->class->fits_width (d->width);
161   fits_length = t->class->fits_length (d->length);
162   if (!fits_width || !fits_length) 
163     {
164       int tl, tr, tt, tb;
165       tl = fits_width ? hl : 0;
166       tr = fits_width ? hr : 0;
167       tt = fits_length ? ht : 0;
168       tb = fits_length ? hb : 0;
169       t->class->set_headers (tl, tr, tt, tb);
170       t->class->driver (d);
171       t->class->area (&tw, &th);
172     }
173   
174   if (!(flags & SOMF_NO_SPACING) && d->cp_y != 0)
175     d->cp_y += d->font_height;
176         
177   if (cs != SOM_COL_NONE
178       && 2 * (tw + d->prop_em_width) <= d->width
179       && nr - (ht + hb) > 5)
180     render_columns ();
181   else if (tw < d->width && th + d->cp_y < d->length)
182     render_simple ();
183   else 
184     render_segments ();
185
186   t->class->set_headers (hl, hr, ht, hb);
187 }
188
189 /* Render the table into multiple columns. */
190 static void
191 render_columns (void)
192 {
193   int y0, y1;
194   int max_len = 0;
195   int index = 0;
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 (void)
239 {
240   assert (d->cp_x == 0);
241   assert (tw < d->width && th + d->cp_y < d->length);
242
243   t->class->title (0, 0);
244   t->class->render (hl, ht, nc - hr, nr - hb);
245   d->cp_y += th;
246 }
247
248 /* General table breaking routine. */
249 static void
250 render_segments (void)
251 {
252   int count = 0;
253   
254   int x_index;
255   int x0, x1;
256   
257   assert (d->cp_x == 0);
258
259   for (x_index = 0, x0 = hl; x0 < nc - hr; x0 = x1, x_index++)
260     {
261       int y_index;
262       int y0, y1;
263       
264       t->class->cumulate (SOM_COLUMNS, x0, &x1, d->width, NULL);
265       if (x_index == 0 && x1 != nc - hr)
266         x_index++;
267
268       for (y_index = 0, y0 = ht; y0 < nr - hb; y0 = y1, y_index++)
269         {
270           int len;
271       
272           if (count++ != 0 && d->cp_y != 0)
273             d->cp_y += d->font_height;
274               
275           t->class->cumulate (SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
276           if (y_index == 0 && y1 != nr - hb)
277             y_index++;
278
279           if (y0 == y1)
280             {
281               assert (d->cp_y);
282               outp_eject_page (d);
283             }
284           else
285             {
286               t->class->title (x_index ? x_index : y_index,
287                                x_index ? y_index : 0);
288               t->class->render (x0, y0, x1, y1);
289           
290               d->cp_y += len;
291             }
292         }
293     }
294 }