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