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