LIST: Remove WEIGHT subcommand.
[pspp-builds.git] / src / output / manager.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <output/manager.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23
24 #include <libpspp/assertion.h>
25 #include <output/output.h>
26
27 #include "gl/xalloc.h"
28
29 /* Table. */
30 static int table_num = 1;
31 static int subtable_num;
32
33 /* Name of PSPP's current command, or NULL if outside a command. */
34 static char *command_name;
35 \f
36 struct som_entity *
37 som_entity_clone (struct som_entity *entity)
38 {
39   struct som_entity *copy = xmemdup (entity, sizeof *entity);
40   copy->command_name = xstrdup (entity->command_name);
41   return copy;
42 }
43
44 void
45 som_entity_destroy (struct som_entity *entity)
46 {
47   if (entity != NULL)
48     {
49       free (entity->command_name);
50       free (entity);
51     }
52 }
53
54 /* Increments table_num so different procedures' output can be
55    distinguished. */
56 void
57 som_new_series (void)
58 {
59   if (subtable_num != 0)
60     {
61       table_num++;
62       subtable_num = 0;
63     }
64 }
65
66 /* Sets COMMAND_NAME as the name of the current command,
67    for embedding in output. */
68 void
69 som_set_command_name (const char *command_name_)
70 {
71   free (command_name);
72   command_name = command_name_ ? xstrdup (command_name_) : NULL;
73 }
74
75 /* Ejects the paper for all active devices. */
76 void
77 som_eject_page (void)
78 {
79   struct outp_driver *d;
80
81   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
82     outp_eject_page (d);
83 }
84
85 /* Flushes output on all active devices. */
86 void
87 som_flush (void)
88 {
89   struct outp_driver *d;
90
91   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
92     outp_flush (d);
93 }
94
95 /* Skip down a single line on all active devices. */
96 void
97 som_blank_line (void)
98 {
99   struct outp_driver *d;
100
101   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
102     if (d->page_open && d->cp_y != 0)
103       d->cp_y += d->font_height;
104 }
105 \f
106 static void render_columns (void *r, struct outp_driver *, struct som_entity *,
107                             int tw, int th,
108                             int hl, int hr, int ht, int hb);
109 static void render_simple (void *r, struct outp_driver *, struct som_entity *,
110                            int tw, int th,
111                            int hl, int hr, int ht, int hb);
112 static void render_segments (void *r, struct outp_driver *,
113                              struct som_entity *,
114                              int tw, int th,
115                              int hl, int hr, int ht, int hb);
116
117 static void output_entity (struct outp_driver *, struct som_entity *);
118
119 /* Output table T to appropriate output devices. */
120 void
121 som_submit (struct som_entity *t)
122 {
123   struct outp_driver *d;
124   unsigned int flags;
125
126 #if DEBUGGING
127   static int entry;
128
129   assert (entry++ == 0);
130 #endif
131
132   t->class->flags (t, &flags);
133   if (!(flags & SOMF_NO_TITLE))
134     subtable_num++;
135   t->table_num = table_num;
136   t->subtable_num = subtable_num;
137   t->command_name = command_name ? xstrdup (command_name) : NULL;
138
139   if (t->type == SOM_TABLE)
140     {
141       int hl, hr, ht, hb;
142       int nc, nr;
143
144       t->class->count (t, &nc, &nr);
145       t->class->headers (t, &hl, &hr, &ht, &hb);
146       if (hl + hr > nc || ht + hb > nr)
147         {
148           fprintf (stderr, "headers: (l,r)=(%d,%d), (t,b)=(%d,%d) "
149                    "in table size (%d,%d)\n",
150                    hl, hr, ht, hb, nc, nr);
151           NOT_REACHED ();
152         }
153       else if (hl + hr == nc)
154         fprintf (stderr, "warning: headers (l,r)=(%d,%d) in table width %d\n",
155                 hl, hr, nc);
156       else if (ht + hb == nr)
157         fprintf (stderr, "warning: headers (t,b)=(%d,%d) in table height %d\n",
158                 ht, hb, nr);
159     }
160
161   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
162     output_entity (d, t);
163
164 #if DEBUGGING
165   assert (--entry == 0);
166 #endif
167 }
168
169 static bool
170 check_fits_width (struct som_entity *t, const struct outp_driver *d, void *r)
171 {
172   int hl, hr, ht, hb;
173   int nc, nr;
174   int i;
175
176   t->class->headers (t, &hl, &hr, &ht, &hb);
177   t->class->count (t, &nc, &nr);
178   for (i = hl; i < nc - hr; i++)
179     {
180       int end, actual;
181       t->class->cumulate (r, SOM_COLUMNS, i, &end, d->width, &actual);
182       if (end == i)
183         return false;
184     }
185
186   return true;
187 }
188
189 static bool
190 check_fits_length (struct som_entity *t, const struct outp_driver *d, void *r)
191 {
192   int hl, hr, ht, hb;
193   int nc, nr;
194   int i;
195
196   t->class->headers (t, &hl, &hr, &ht, &hb);
197   t->class->count (t, &nc, &nr);
198   for (i = ht; i < nr - hb; i++)
199     {
200       int end, actual;
201       t->class->cumulate (r, SOM_ROWS, i, &end, d->length, &actual);
202       if (end == i)
203         return false;
204     }
205
206   return true;
207 }
208
209 /* Output entity T to driver D. */
210 static void
211 output_entity (struct outp_driver *d, struct som_entity *t)
212 {
213   bool fits_width, fits_length;
214   unsigned int flags;
215   int hl, hr, ht, hb;
216   int tw, th;
217   int nc, nr;
218   int cs;
219   void *r;
220
221   outp_open_page (d);
222   if (d->class->special)
223     {
224       d->class->submit (d, t);
225       return;
226     }
227
228   t->class->headers (t, &hl, &hr, &ht, &hb);
229   t->class->count (t, &nc, &nr);
230   t->class->columns (t, &cs);
231   t->class->flags (t, &flags);
232
233   r = t->class->render_init (t, d, hl, hr, ht, hb);
234
235   fits_width = check_fits_width (t, d, r);
236   fits_length = check_fits_length (t, d, r);
237   if (!fits_width || !fits_length)
238     {
239       t->class->render_free (r);
240
241       if (!fits_width)
242         hl = hr = 0;
243       if (!fits_length)
244         ht = hb = 0;
245
246       r = t->class->render_init (t, d, hl, hr, ht, hb);
247     }
248   t->class->area (r, &tw, &th);
249
250   if (!(flags & SOMF_NO_SPACING) && d->cp_y != 0)
251     d->cp_y += d->font_height;
252
253   if (cs != SOM_COL_NONE
254       && 2 * (tw + d->prop_em_width) <= d->width
255       && nr - (ht + hb) > 5)
256     render_columns (r, d, t, tw, th, hl, hr, ht, hb);
257   else if (tw < d->width && th + d->cp_y < d->length)
258     render_simple (r, d, t, tw, th, hl, hr, ht, hb);
259   else
260     render_segments (r, d, t, tw, th, hl, hr, ht, hb);
261
262   t->class->render_free (r);
263 }
264
265 /* Render the table into multiple columns. */
266 static void
267 render_columns (void *r, struct outp_driver *d, struct som_entity *t,
268                 int tw, int th UNUSED,
269                 int hl UNUSED, int hr UNUSED, int ht, int hb)
270 {
271   int y0, y1;
272   int max_len = 0;
273   int index = 0;
274   int nc, nr;
275   int cs;
276
277   t->class->count (t, &nc, &nr);
278   t->class->columns (t, &cs);
279
280   assert (cs == SOM_COL_DOWN);
281   assert (d->cp_x == 0);
282
283   for (y0 = ht; y0 < nr - hb; y0 = y1)
284     {
285       int len;
286
287       t->class->cumulate (r, SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
288
289       if (y0 == y1)
290         {
291           assert (d->cp_y);
292           outp_eject_page (d);
293         }
294       else
295         {
296           if (len > max_len)
297             max_len = len;
298
299           t->class->title (r, index++, 0, t->table_num, t->subtable_num,
300                            t->command_name);
301           t->class->render (r, 0, y0, nc, y1);
302
303           d->cp_x += tw + 2 * d->prop_em_width;
304           if (d->cp_x + tw > d->width)
305             {
306               d->cp_x = 0;
307               d->cp_y += max_len;
308               max_len = 0;
309             }
310         }
311     }
312
313   if (d->cp_x > 0)
314     {
315       d->cp_x = 0;
316       d->cp_y += max_len;
317     }
318 }
319
320 /* Render the table by itself on the current page. */
321 static void
322 render_simple (void *r, struct outp_driver *d, struct som_entity *t,
323                int tw, int th,
324                int hl, int hr, int ht, int hb)
325 {
326   int nc, nr;
327
328   t->class->count (t, &nc, &nr);
329
330   assert (d->cp_x == 0);
331   assert (tw < d->width && th + d->cp_y < d->length);
332
333   t->class->title (r, 0, 0, t->table_num, t->subtable_num, t->command_name);
334   t->class->render (r, hl, ht, nc - hr, nr - hb);
335   d->cp_y += th;
336 }
337
338 /* General table breaking routine. */
339 static void
340 render_segments (void *r, struct outp_driver *d, struct som_entity *t,
341                  int tw UNUSED, int th UNUSED,
342                  int hl, int hr, int ht, int hb)
343 {
344   int count = 0;
345
346   int x_index;
347   int x0, x1;
348
349   int nc, nr;
350
351   assert (d->cp_x == 0);
352
353   t->class->count (t, &nc, &nr);
354   for (x_index = 0, x0 = hl; x0 < nc - hr; x0 = x1, x_index++)
355     {
356       int y_index;
357       int y0, y1;
358
359       t->class->cumulate (r, SOM_COLUMNS, x0, &x1, d->width, NULL);
360       if (x_index == 0 && x1 != nc - hr)
361         x_index++;
362
363       for (y_index = 0, y0 = ht; y0 < nr - hb; y0 = y1, y_index++)
364         {
365           int len;
366
367           if (count++ != 0 && d->cp_y != 0)
368             d->cp_y += d->font_height;
369
370           t->class->cumulate (r, SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
371           if (y_index == 0 && y1 != nr - hb)
372             y_index++;
373
374           if (y0 == y1)
375             {
376               assert (d->cp_y);
377               outp_eject_page (d);
378             }
379           else
380             {
381               t->class->title (r, x_index ? x_index : y_index,
382                                x_index ? y_index : 0,
383                                t->table_num, t->subtable_num, t->command_name);
384               t->class->render (r, x0, y0, x1, y1);
385
386               d->cp_y += len;
387             }
388         }
389     }
390 }