output: Remove support for bottom and right side headers.
[pspp] / src / output / table.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2011, 2014, 2016 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/table.h"
20 #include "output/table-provider.h"
21
22 #include <assert.h>
23 #include <inttypes.h>
24 #include <stdlib.h>
25
26 #include "data/format.h"
27 #include "libpspp/assertion.h"
28 #include "libpspp/cast.h"
29 #include "libpspp/compiler.h"
30 #include "libpspp/pool.h"
31 #include "libpspp/str.h"
32 #include "output/output-item.h"
33 #include "output/pivot-table.h"
34 #include "output/table.h"
35
36 #include "gl/xalloc.h"
37
38 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
39 #define H TABLE_HORZ
40 #define V TABLE_VERT
41
42 /* Increases TABLE's reference count, indicating that it has an additional
43    owner.  An table that is shared among multiple owners must not be
44    modified. */
45 struct table *
46 table_ref (const struct table *table_)
47 {
48   struct table *table = CONST_CAST (struct table *, table_);
49   table->ref_cnt++;
50   return table;
51 }
52
53 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
54    If TABLE no longer has any owners, it is freed. */
55 void
56 table_unref (struct table *table)
57 {
58   if (table != NULL)
59     {
60       assert (table->ref_cnt > 0);
61       if (--table->ref_cnt == 0)
62         pool_destroy (table->container);
63     }
64 }
65
66 /* Returns true if TABLE has more than one owner.  A table item that is shared
67    among multiple owners must not be modified. */
68 bool
69 table_is_shared (const struct table *table)
70 {
71   return table->ref_cnt > 1;
72 }
73 \f
74 struct table_area_style *
75 table_area_style_clone (struct pool *pool, const struct table_area_style *old)
76 {
77   struct table_area_style *new = pool_malloc (pool, sizeof *new);
78   *new = *old;
79   if (new->font_style.typeface)
80     new->font_style.typeface = pool_strdup (pool, new->font_style.typeface);
81   return new;
82 }
83
84 void
85 table_area_style_free (struct table_area_style *style)
86 {
87   if (style)
88     {
89       free (style->font_style.typeface);
90       free (style);
91     }
92 }
93 \f
94 const char *
95 table_halign_to_string (enum table_halign halign)
96 {
97   switch (halign)
98     {
99     case TABLE_HALIGN_LEFT: return "left";
100     case TABLE_HALIGN_CENTER: return "center";
101     case TABLE_HALIGN_RIGHT: return "right";
102     case TABLE_HALIGN_DECIMAL: return "decimal";
103     case TABLE_HALIGN_MIXED: return "mixed";
104     default: return "**error**";
105     }
106 }
107
108 const char *
109 table_valign_to_string (enum table_valign valign)
110 {
111   switch (valign)
112     {
113     case TABLE_VALIGN_TOP: return "top";
114     case TABLE_VALIGN_CENTER: return "center";
115     case TABLE_VALIGN_BOTTOM: return "bottom";
116     default: return "**error**";
117     }
118 }
119
120 enum table_halign
121 table_halign_interpret (enum table_halign halign, bool numeric)
122 {
123   switch (halign)
124     {
125     case TABLE_HALIGN_LEFT:
126     case TABLE_HALIGN_CENTER:
127     case TABLE_HALIGN_RIGHT:
128       return halign;
129
130     case TABLE_HALIGN_MIXED:
131       return numeric ? TABLE_HALIGN_RIGHT : TABLE_HALIGN_LEFT;
132
133     case TABLE_HALIGN_DECIMAL:
134       return TABLE_HALIGN_DECIMAL;
135
136     default:
137       NOT_REACHED ();
138     }
139 }
140
141 void
142 font_style_copy (struct pool *container,
143                  struct font_style *dst, const struct font_style *src)
144 {
145   *dst = *src;
146   if (dst->typeface)
147     dst->typeface = pool_strdup (container, dst->typeface);
148 }
149
150 void
151 font_style_uninit (struct font_style *font)
152 {
153   if (font)
154     free (font->typeface);
155 }
156
157 void
158 table_area_style_copy (struct pool *container, struct table_area_style *dst,
159                        const struct table_area_style *src)
160 {
161   font_style_copy (container, &dst->font_style, &src->font_style);
162   dst->cell_style = src->cell_style;
163 }
164
165 void
166 table_area_style_uninit (struct table_area_style *area)
167 {
168   if (area)
169     font_style_uninit (&area->font_style);
170 }
171
172 const char *
173 table_stroke_to_string (enum table_stroke stroke)
174 {
175   switch (stroke)
176     {
177     case TABLE_STROKE_NONE: return "none";
178     case TABLE_STROKE_SOLID: return "solid";
179     case TABLE_STROKE_DASHED: return "dashed";
180     case TABLE_STROKE_THICK: return "thick";
181     case TABLE_STROKE_THIN: return "thin";
182     case TABLE_STROKE_DOUBLE: return "double";
183     default:
184       return "**error**";
185     }
186 }
187
188 void
189 cell_color_dump (const struct cell_color *c)
190 {
191   if (c->alpha != 255)
192     printf ("rgba(%d, %d, %d, %d)", c->r, c->g, c->b, c->alpha);
193   else
194     printf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8, c->r, c->g, c->b);
195 }
196
197 void
198 font_style_dump (const struct font_style *f)
199 {
200   printf ("%s %dpx ", f->typeface, f->size);
201   cell_color_dump (&f->fg[0]);
202   putchar ('/');
203   cell_color_dump (&f->bg[0]);
204   if (!cell_color_equal (f->fg[0], f->fg[1])
205       || !cell_color_equal (f->bg[0], f->bg[1]))
206     {
207       printf (" alt=");
208       cell_color_dump (&f->fg[1]);
209       putchar ('/');
210       cell_color_dump (&f->bg[1]);
211     }
212   if (f->bold)
213     fputs (" bold", stdout);
214   if (f->italic)
215     fputs (" italic", stdout);
216   if (f->underline)
217     fputs (" underline", stdout);
218 }
219
220 bool
221 font_style_equal (const struct font_style *a, const struct font_style *b)
222 {
223   return (a->bold == b->bold
224           && a->italic == b->italic
225           && a->underline == b->underline
226           && a->markup == b->markup
227           && cell_color_equal (a->fg[0], b->fg[0])
228           && cell_color_equal (a->fg[1], b->fg[1])
229           && cell_color_equal (a->bg[0], b->bg[0])
230           && cell_color_equal (a->bg[1], b->bg[1])
231           && !strcmp (a->typeface ? a->typeface : "",
232                       b->typeface ? b->typeface : "")
233           && a->size == b->size);
234 }
235
236 void
237 cell_style_dump (const struct cell_style *c)
238 {
239   fputs (table_halign_to_string (c->halign), stdout);
240   if (c->halign == TABLE_HALIGN_DECIMAL)
241     printf ("(%.2gpx)", c->decimal_offset);
242   printf (" %s", table_valign_to_string (c->valign));
243   printf (" %d,%d,%d,%dpx",
244           c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1],
245           c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]);
246 }
247 \f
248 /* Creates and returns a new table with NC columns and NR rows and initially no
249    header rows or columns.
250
251    Sets the number of header rows on each side of TABLE to HL on the
252    left, HT on the top.  Header rows
253    are repeated when a table is broken across multiple columns or
254    multiple pages.
255
256    The table's cells are initially empty. */
257 struct table *
258 table_create (int nc, int nr, int hl, int ht)
259 {
260   struct pool *pool = pool_create ();
261   struct table *t = pool_alloc (pool, sizeof *t);
262   *t = (struct table) {
263     .container = pool,
264     .n = { [H] = nc, [V] = nr },
265     .h = { [H] = hl, [V] = ht },
266     .ref_cnt = 1,
267     .cc = pool_calloc (pool, nr * nc, sizeof *t->cc),
268     .cp = pool_calloc (pool, nr * nc, sizeof *t->cp),
269     .rh = pool_calloc (pool, nc, nr + 1),
270     .rv = pool_calloc (pool, nr, nc + 1),
271   };
272   return t;
273 }
274 \f
275 /* Rules. */
276
277 /* Draws a vertical line to the left of cells at horizontal position X
278    from Y1 to Y2 inclusive in style STYLE. */
279 void
280 table_vline (struct table *t, int style, int x, int y1, int y2)
281 {
282   if (x < 0 || x > t->n[H] || y1 < 0 || y1 > y2 || y2 >= t->n[V])
283     {
284       printf ("bad vline: x=%d y=(%d,%d) in table size (%d,%d)\n",
285               x, y1, y2, t->n[H], t->n[V]);
286       abort ();
287     }
288
289   for (int y = y1; y <= y2; y++)
290     t->rv[x + (t->n[H] + 1) * y] = style;
291 }
292
293 /* Draws a horizontal line above cells at vertical position Y from X1
294    to X2 inclusive in style STYLE. */
295 void
296 table_hline (struct table *t, int style, int x1, int x2, int y)
297 {
298   if (y < 0 || y > t->n[V] || x1 < 0 || x1 > x2 || x2 >= t->n[H])
299     {
300       printf ("bad hline: x=(%d,%d) y=%d in table size (%d,%d)\n",
301               x1, x2, y, t->n[H], t->n[V]);
302       abort ();
303     }
304
305   for (int x = x1; x <= x2; x++)
306     t->rh[x + t->n[H] * y] = style;
307 }
308 \f
309 /* Cells. */
310
311 /* Fill TABLE cells (X1,X2)-(Y1,Y2), inclusive, with VALUE and OPT. */
312 void
313 table_put (struct table *table, int x1, int y1, int x2, int y2,
314            unsigned int opt, const struct pivot_value *value)
315 {
316   assert (0 <= x1 && x1 <= x2 && x2 < table->n[H]);
317   assert (0 <= y1 && y1 <= y2 && y2 < table->n[V]);
318
319   const bool debugging = false;
320   if (debugging)
321     {
322       printf ("put ");
323       if (x1 == x2)
324         printf ("%d", x1);
325       else
326         printf ("%d-%d", x1, x2);
327       printf (",");
328       if (y1 == y2)
329         printf ("%d", y1);
330       else
331         printf ("%d-%d", y1, y2);
332
333       char *value_s = value ? pivot_value_to_string (value, NULL) : NULL;
334       printf (": \"%s\"\n", value_s ? value_s : "");
335       free (value_s);
336     }
337
338   if (x1 == x2 && y1 == y2)
339     {
340       table->cc[x1 + y1 * table->n[H]] = CONST_CAST (struct pivot_value *, value);
341       table->cp[x1 + y1 * table->n[H]] = opt;
342     }
343   else
344     {
345       struct table_cell *cell = pool_alloc (table->container, sizeof *cell);
346       *cell = (struct table_cell) {
347         .d = { [H] = { x1, x2 + 1 }, [V] = { y1, y2 + 1 } },
348         .options = opt,
349         .value = value,
350       };
351
352       for (int y = y1; y <= y2; y++)
353         {
354           size_t ofs = x1 + y * table->n[H];
355           void **cc = &table->cc[ofs];
356           unsigned char *ct = &table->cp[ofs];
357           for (int x = x1; x <= x2; x++)
358             {
359               *cc++ = cell;
360               *ct++ = opt | TABLE_CELL_JOIN;
361             }
362         }
363     }
364 }
365
366 static void
367 free_value (void *value_)
368 {
369   struct pivot_value *value = value_;
370   pivot_value_destroy (value);
371 }
372
373 void
374 table_put_owned (struct table *table, int x1, int y1, int x2, int y2,
375                  unsigned opt, struct pivot_value *value)
376 {
377   table_put (table, x1, y1, x2, y2, opt, value);
378   pool_register (table->container, free_value, value);
379 }
380
381 /* Returns true if column C, row R has no contents, otherwise false. */
382 bool
383 table_cell_is_empty (const struct table *table, int c, int r)
384 {
385   return table->cc[c + r * table->n[H]] == NULL;
386 }
387 \f
388 /* Initializes CELL with the contents of the table cell at column X and row Y
389    within TABLE. */
390 void
391 table_get_cell (const struct table *t, int x, int y, struct table_cell *cell)
392 {
393   assert (x >= 0 && x < t->n[TABLE_HORZ]);
394   assert (y >= 0 && y < t->n[TABLE_VERT]);
395
396   int index = x + y * t->n[H];
397   unsigned char opt = t->cp[index];
398   const void *cc = t->cc[index];
399
400   struct table_area_style *style
401     = t->styles[(opt & TABLE_CELL_STYLE_MASK) >> TABLE_CELL_STYLE_SHIFT];
402
403   static const struct pivot_value empty_value = {
404     .text = {
405       .type = PIVOT_VALUE_TEXT,
406       .local = (char *) "",
407       .c = (char *) "",
408       .id = (char *) "",
409       .user_provided = true,
410     },
411   };
412
413   if (opt & TABLE_CELL_JOIN)
414     {
415       const struct table_cell *jc = cc;
416       *cell = *jc;
417       if (!cell->value)
418         cell->value = &empty_value;
419       if (!cell->font_style)
420         cell->font_style = &style->font_style;
421       if (!cell->cell_style)
422         cell->cell_style = &style->cell_style;
423     }
424   else
425     {
426       const struct pivot_value *v = cc ? cc : &empty_value;
427       const struct pivot_value_ex *ex = pivot_value_ex (v);
428       *cell = (struct table_cell) {
429         .d = { [H] = { x, x + 1 }, [V] = { y, y + 1 } },
430         .options = opt,
431         .value = v,
432         .font_style = ex->font_style ? ex->font_style : &style->font_style,
433         .cell_style = ex->cell_style ? ex->cell_style : &style->cell_style,
434       };
435     }
436
437   assert (cell->font_style);
438   assert (cell->cell_style);
439 }
440
441 /* Returns one of the TABLE_STROKE_* enumeration constants (declared in
442    output/table.h) representing a rule running alongside one of the cells in
443    TABLE.
444
445    Suppose NC is the number of columns in TABLE and NR is the number of rows.
446    Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
447    (0,0), the return value is the rule that runs vertically on the left side of
448    cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
449    cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
450    cell (NC-1,0).
451
452    The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
453    within a 7x7 table.  The '|' characters at the intersection of the X labels
454    and Y labels show the rule whose style would be returned by calling
455    table_get_rule with those X and Y values:
456
457                            0  1  2  3  4  5  6  7
458                            +--+--+--+--+--+--+--+
459                          0 |  |  |  |  |  |  |  |
460                            +--+--+--+--+--+--+--+
461                          1 |  |  |  |  |  |  |  |
462                            +--+--+--+--+--+--+--+
463                          2 |  |  |  |  |  |  |  |
464                            +--+--+--+--+--+--+--+
465                          3 |  |  |  |  |  |  |  |
466                            +--+--+--+--+--+--+--+
467                          4 |  |  |  |  |  |  |  |
468                            +--+--+--+--+--+--+--+
469                          5 |  |  |  |  |  |  |  |
470                            +--+--+--+--+--+--+--+
471                          6 |  |  |  |  |  |  |  |
472                            +--+--+--+--+--+--+--+
473
474    Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
475    (X,Y) = (0,0), the return value is the rule that runs horizontally above
476    the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
477    between that cell and cell (0,1); and so on, up to (0,NR), which runs
478    horizontally below cell (0,NR-1). */
479 struct table_border_style
480 table_get_rule (const struct table *table, enum table_axis axis, int x, int y)
481 {
482   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
483   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
484
485   size_t border_idx = (axis == TABLE_VERT
486                       ? table->rh[x + table->n[H] * y]
487                       : table->rv[x + (table->n[H] + 1) * y]);
488   return (border_idx < table->n_borders
489           ? table->borders[border_idx]
490           : (struct table_border_style) { TABLE_STROKE_NONE,
491                                           CELL_COLOR_BLACK });
492 }