Revise.
[pspp] / src / som.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 "som.h"
22 #include "error.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include "output.h"
26 #include "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 GLOBAL_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 GLOBAL_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 GLOBAL_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   assert (d->driver_open);
151   if (!d->page_open && !d->class->open_page (d))
152     {
153       d->device = OUTP_DEV_DISABLED;
154       return;
155     }
156   
157   if (d->class->special || entity->type == SOM_CHART)
158     {
159       driver->class->submit (d, entity);
160       return;
161     }
162
163   t = entity;
164   
165   t->class->driver (d);
166   t->class->area (&tw, &th);
167   fits_width = t->class->fits_width (d->width);
168   fits_length = t->class->fits_length (d->length);
169   if (!fits_width || !fits_length) 
170     {
171       int tl, tr, tt, tb;
172       tl = fits_width ? hl : 0;
173       tr = fits_width ? hr : 0;
174       tt = fits_length ? ht : 0;
175       tb = fits_length ? hb : 0;
176       t->class->set_headers (tl, tr, tt, tb);
177       t->class->driver (d);
178       t->class->area (&tw, &th);
179     }
180   
181   if (!(flags & SOMF_NO_SPACING) && d->cp_y != 0)
182     d->cp_y += d->font_height;
183         
184   if (cs != SOM_COL_NONE
185       && 2 * (tw + d->prop_em_width) <= d->width
186       && nr - (ht + hb) > 5)
187     render_columns ();
188   else if (tw < d->width && th + d->cp_y < d->length)
189     render_simple ();
190   else 
191     render_segments ();
192
193   t->class->set_headers (hl, hr, ht, hb);
194 }
195
196 /* Render the table into multiple columns. */
197 static void
198 render_columns (void)
199 {
200   int y0, y1;
201   int max_len = 0;
202   int index = 0;
203   
204   assert (cs == SOM_COL_DOWN);
205   assert (d->cp_x == 0);
206
207   for (y0 = ht; y0 < nr - hb; y0 = y1)
208     {
209       int len;
210       
211       t->class->cumulate (SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
212
213       if (y0 == y1)
214         {
215           assert (d->cp_y);
216           outp_eject_page (d);
217         } else {
218           if (len > max_len)
219             max_len = len;
220
221           t->class->title (index++, 0);
222           t->class->render (0, y0, nc, y1);
223           
224           d->cp_x += tw + 2 * d->prop_em_width;
225           if (d->cp_x + tw > d->width)
226             {
227               d->cp_x = 0;
228               d->cp_y += max_len;
229               max_len = 0;
230             }
231         }
232     }
233   
234   if (d->cp_x > 0)
235     {
236       d->cp_x = 0;
237       d->cp_y += max_len;
238     }
239 }
240
241 /* Render the table by itself on the current page. */
242 static void
243 render_simple (void)
244 {
245   assert (d->cp_x == 0);
246   assert (tw < d->width && th + d->cp_y < d->length);
247
248   t->class->title (0, 0);
249   t->class->render (hl, ht, nc - hr, nr - hb);
250   d->cp_y += th;
251 }
252
253 /* General table breaking routine. */
254 static void
255 render_segments (void)
256 {
257   int count = 0;
258   
259   int x_index;
260   int x0, x1;
261   
262   assert (d->cp_x == 0);
263
264   for (x_index = 0, x0 = hl; x0 < nc - hr; x0 = x1, x_index++)
265     {
266       int y_index;
267       int y0, y1;
268       
269       t->class->cumulate (SOM_COLUMNS, x0, &x1, d->width, NULL);
270       if (x_index == 0 && x1 != nc - hr)
271         x_index++;
272
273       for (y_index = 0, y0 = ht; y0 < nr - hb; y0 = y1, y_index++)
274         {
275           int len;
276       
277           if (count++ != 0 && d->cp_y != 0)
278             d->cp_y += d->font_height;
279               
280           t->class->cumulate (SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
281           if (y_index == 0 && y1 != nr - hb)
282             y_index++;
283
284           if (y0 == y1)
285             {
286               assert (d->cp_y);
287               outp_eject_page (d);
288             } else {
289               t->class->title (x_index ? x_index : y_index,
290                                x_index ? y_index : 0);
291               t->class->render (x0, y0, x1, y1);
292           
293               d->cp_y += len;
294             }
295         }
296     }
297 }