Fri Dec 12 23:54:37 2003 Ben Pfaff <blp@gnu.org>
[pspp-builds.git] / 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., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include <assert.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include "output.h"
25 #include "som.h"
26 /*#undef DEBUGGING*/
27 /*#define DEBUGGING 1 */
28 #include "debug-print.h"
29
30 /* Table. */
31 int table_num = 1;
32 int subtable_num;
33 \f
34 /* Increments table_num so different procedures' output can be
35    distinguished. */
36 void
37 som_new_series (void)
38 {
39   if (subtable_num != 0)
40     {
41       table_num++;
42       subtable_num = 0;
43     }
44 }
45
46 /* Ejects the paper for all active devices. */
47 void
48 som_eject_page (void)
49 {
50   struct outp_driver *d;
51
52   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
53     outp_eject_page (d);
54 }
55
56 /* Skip down a single line on all active devices. */
57 void
58 som_blank_line (void)
59 {
60   struct outp_driver *d;
61   
62   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
63     if (d->page_open && d->cp_y != 0)
64       d->cp_y += d->font_height;
65 }
66 \f
67 /* Driver. */
68 static struct outp_driver *d=0;
69
70 /* Table. */
71 static struct som_table *t=0;
72
73 /* Flags. */
74 static unsigned flags;
75
76 /* Number of columns, rows. */
77 static int nc, nr;
78
79 /* Number of columns or rows in left, right, top, bottom headers. */
80 static int hl, hr, ht, hb;
81
82 /* Column style. */
83 static int cs;
84
85 /* Table height, width. */
86 static int th, tw;
87
88 static void render_columns (void);
89 static void render_simple (void);
90 static void render_segments (void);
91
92 static void output_table (struct outp_driver *, struct som_table *);
93
94 /* Output table T to appropriate output devices. */
95 void
96 som_submit (struct som_table *t)
97 {
98 #if GLOBAL_DEBUGGING
99   static int entry;
100   
101   assert (entry++ == 0);
102 #endif
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 #if GLOBAL_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       abort ();
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     struct outp_driver *d;
129
130     for (d = outp_drivers (NULL); d; d = outp_drivers (d))
131       output_table (d, t);
132   }
133   
134 #if GLOBAL_DEBUGGING
135   assert (--entry == 0);
136 #endif
137 }
138
139 /* Output table TABLE to driver DRIVER. */
140 static void
141 output_table (struct outp_driver *driver, struct som_table *table)
142 {
143   d = driver;
144   t = table;
145
146   assert (d->driver_open);
147   if (!d->page_open && !d->class->open_page (d))
148     {
149       d->device = OUTP_DEV_DISABLED;
150       return;
151     }
152   
153   if (d->class->special)
154     {
155       driver->class->submit (d, t);
156       return;
157     }
158   
159   t->class->driver (d);
160   t->class->area (&tw, &th);
161   
162   if (!(flags & SOMF_NO_SPACING) && d->cp_y != 0)
163     d->cp_y += d->font_height;
164         
165   if (cs != SOM_COL_NONE
166       && 2 * (tw + d->prop_em_width) <= d->width
167       && nr - (ht + hb) > 5)
168     render_columns ();
169   else if (tw < d->width && th + d->cp_y < d->length)
170     render_simple ();
171   else 
172     render_segments ();
173 }
174
175 /* Render the table into multiple columns. */
176 static void
177 render_columns (void)
178 {
179   int y0, y1;
180   int max_len = 0;
181   int index = 0;
182   
183   assert (cs == SOM_COL_DOWN);
184   assert (d->cp_x == 0);
185
186   for (y0 = ht; y0 < nr - hb; y0 = y1)
187     {
188       int len;
189       
190       t->class->cumulate (SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
191
192       if (y0 == y1)
193         {
194           assert (d->cp_y);
195           outp_eject_page (d);
196         } else {
197           if (len > max_len)
198             max_len = len;
199
200           t->class->title (index++, 0);
201           t->class->render (0, y0, nc, y1);
202           
203           d->cp_x += tw + 2 * d->prop_em_width;
204           if (d->cp_x + tw > d->width)
205             {
206               d->cp_x = 0;
207               d->cp_y += max_len;
208               max_len = 0;
209             }
210         }
211     }
212   
213   if (d->cp_x > 0)
214     {
215       d->cp_x = 0;
216       d->cp_y += max_len;
217     }
218 }
219
220 /* Render the table by itself on the current page. */
221 static void
222 render_simple (void)
223 {
224   assert (d->cp_x == 0);
225   assert (tw < d->width && th + d->cp_y < d->length);
226
227   t->class->title (0, 0);
228   t->class->render (hl, ht, nc - hr, nr - hb);
229   d->cp_y += th;
230 }
231
232 /* General table breaking routine. */
233 static void
234 render_segments (void)
235 {
236   int count = 0;
237   
238   int x_index;
239   int x0, x1;
240   
241   assert (d->cp_x == 0);
242
243   for (x_index = 0, x0 = hl; x0 < nc - hr; x0 = x1, x_index++)
244     {
245       int y_index;
246       int y0, y1;
247       
248       t->class->cumulate (SOM_COLUMNS, x0, &x1, d->width, NULL);
249       if (x_index == 0 && x1 != nc - hr)
250         x_index++;
251
252       for (y_index = 0, y0 = ht; y0 < nr - hb; y0 = y1, y_index++)
253         {
254           int len;
255       
256           if (count++ != 0 && d->cp_y != 0)
257             d->cp_y += d->font_height;
258               
259           t->class->cumulate (SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
260           if (y_index == 0 && y1 != nr - hb)
261             y_index++;
262
263           if (y0 == y1)
264             {
265               assert (d->cp_y);
266               outp_eject_page (d);
267             } else {
268               t->class->title (x_index ? x_index : y_index,
269                                x_index ? y_index : 0);
270               t->class->render (x0, y0, x1, y1);
271           
272               d->cp_y += len;
273             }
274         }
275     }
276 }