1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2018 Free Software Foundation, Inc.
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.
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.
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/>. */
21 #include "output/pivot-table.h"
23 #include "data/settings.h"
24 #include "libpspp/assertion.h"
25 #include "libpspp/pool.h"
26 #include "output/table.h"
27 #include "output/page-eject-item.h"
28 #include "output/table-item.h"
29 #include "output/text-item.h"
30 #include "output/table-provider.h"
32 #include "gl/minmax.h"
33 #include "gl/xalloc.h"
38 static const struct pivot_category *
39 find_category (const struct pivot_dimension *d, int dim_index,
40 const size_t *indexes, int row_ofs)
42 size_t index = indexes[dim_index];
43 assert (index < d->n_leaves);
44 for (const struct pivot_category *c = d->presentation_leaves[index];
47 /* A category can covert multiple rows. Only return the category for its
49 if (row_ofs == c->extra_depth)
52 row_ofs -= 1 + c->extra_depth;
59 static struct table_area_style *
60 table_area_style_override (struct pool *pool,
61 const struct table_area_style *in,
62 const struct cell_style *cell_,
63 const struct font_style *font_,
66 const struct cell_style *cell = cell_ ? cell_ : &in->cell_style;
67 const struct font_style *font = font_ ? font_ : &in->font_style;
69 struct table_area_style *out = (pool
70 ? pool_alloc (pool, sizeof *out)
71 : xmalloc (sizeof *out));
72 *out = (struct table_area_style) {
73 .cell_style.halign = rotate_label ? TABLE_HALIGN_CENTER : cell->halign,
74 .cell_style.valign = rotate_label ? TABLE_VALIGN_CENTER : cell->valign,
75 .cell_style.decimal_offset = cell->decimal_offset,
76 .cell_style.margin[H][0] = cell->margin[H][0],
77 .cell_style.margin[H][1] = cell->margin[H][1],
78 .cell_style.margin[V][0] = cell->margin[V][0],
79 .cell_style.margin[V][1] = cell->margin[V][1],
80 .font_style.fg[0] = font->fg[0],
81 .font_style.fg[1] = font->fg[1],
82 .font_style.bg[0] = font->bg[0],
83 .font_style.bg[1] = font->bg[1],
84 .font_style.typeface = (font->typeface
85 ? pool_strdup (pool, font->typeface)
87 .font_style.size = font->size,
88 .font_style.bold = font->bold,
89 .font_style.italic = font->italic,
90 .font_style.underline = font->underline,
91 .font_style.markup = font->markup,
97 fill_cell (struct table *t, int x1, int y1, int x2, int y2,
98 const struct table_area_style *style, int style_idx,
99 const struct pivot_value *value, struct footnote **footnotes,
100 enum settings_value_show show_values,
101 enum settings_value_show show_variables,
105 struct string s = DS_EMPTY_INITIALIZER;
106 int opts = style_idx << TAB_STYLE_SHIFT;
109 bool numeric = pivot_value_format_body (value, show_values,
113 if (value->font_style && value->font_style->markup)
118 table_joint_text (t, x1, y1, x2, y2, opts, ds_cstr (&s));
123 if (value->cell_style || value->font_style || rotate_label)
124 table_add_style (t, x1, y1,
125 table_area_style_override (t->container, style,
130 for (size_t i = 0; i < value->n_footnotes; i++)
132 struct footnote *f = footnotes[value->footnotes[i]->idx];
134 table_add_footnote (t, x1, y1, f);
137 if (value->n_subscripts)
138 table_add_subscripts (t, x1, y1,
139 value->subscripts, value->n_subscripts);
143 static struct table_item_text *
144 pivot_value_to_table_item_text (const struct pivot_value *value,
145 const struct table_area_style *area,
146 struct footnote **footnotes,
147 enum settings_value_show show_values,
148 enum settings_value_show show_variables)
153 struct string s = DS_EMPTY_INITIALIZER;
154 pivot_value_format_body (value, show_values, show_variables, &s);
156 struct table_item_text *text = xmalloc (sizeof *text);
157 *text = (struct table_item_text) {
158 .content = ds_steal_cstr (&s),
159 .footnotes = xnmalloc (value->n_footnotes, sizeof *text->footnotes),
160 .style = table_area_style_override (
161 NULL, area, value->cell_style, value->font_style, false),
164 for (size_t i = 0; i < value->n_footnotes; i++)
166 struct footnote *f = footnotes[value->footnotes[i]->idx];
168 text->footnotes[text->n_footnotes++] = f;
175 get_table_rule (const struct table_border_style *styles,
176 enum pivot_border style_idx)
178 return styles[style_idx].stroke | (style_idx << TAB_RULE_STYLE_SHIFT);
182 draw_line (struct table *t, const struct table_border_style *styles,
183 enum pivot_border style_idx,
184 enum table_axis axis, int a, int b0, int b1)
186 int rule = get_table_rule (styles, style_idx);
188 table_hline (t, rule, b0, b1, a);
190 table_vline (t, rule, a, b0, b1);
193 /* Fills row or column headings into T.
195 This function uses terminology and variable names for column headings, but
196 it also applies to row headings because it uses variables for the
197 differences, e.g. when for column headings it would use the H axis, it
198 instead uses 'h', which is set to H for column headings and V for row
201 compose_headings (struct table *t,
202 const struct pivot_axis *a_axis, enum table_axis a,
203 const struct pivot_axis *b_axis,
204 const struct table_border_style *borders,
205 enum pivot_border dim_col_horz,
206 enum pivot_border dim_col_vert,
207 enum pivot_border cat_col_horz,
208 enum pivot_border cat_col_vert,
209 const size_t *column_enumeration, size_t n_columns,
210 const struct table_area_style *label_style,
212 const struct table_area_style *corner_style,
213 struct footnote **footnotes,
214 enum settings_value_show show_values,
215 enum settings_value_show show_variables,
216 bool rotate_inner_labels, bool rotate_outer_labels)
218 enum table_axis b = !a;
219 int b_size = a_axis->label_depth;
220 int a_ofs = b_axis->label_depth;
222 if (!a_axis->n_dimensions || !n_columns || !b_size)
225 const int stride = MAX (1, a_axis->n_dimensions);
227 /* Below, we're going to iterate through the dimensions. Each dimension
228 occupies one or more rows in the heading. 'top_row' is the top row of
229 these (and 'top_row + d->label_depth - 1' is the bottom row). */
232 /* We're going to iterate through dimensions and the rows that label them
233 from top to bottom (from outer to inner dimensions). As we move downward,
234 we start drawing vertical rules to separate categories and groups. After
235 we start drawing a vertical rule in a particular horizontal position, it
236 continues until the bottom of the heading. vrules[pos] indicates whether,
237 in our current row, we have already started drawing a vertical rule in
238 horizontal position 'pos'. (There are n_columns + 1 horizontal positions.
239 We allocate all of them for convenience below but only the inner n_columns
240 - 1 of them really matter.)
242 Here's an example that shows how vertical rules continue all the way
245 +-----------------------------------------------------+ __
247 +-----------------+-----------------+-----------------+ |dimension "bbbb"
248 | bbbb1 | bbbb2 | bbbb3 | _|
249 +-----------------+-----------------+-----------------+ __
250 | aaaa | aaaa | aaaa | |
251 +-----+-----+-----+-----+-----+-----+-----+-----+-----+ |dimension "aaaa"
252 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3| _|
253 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
258 |___________________vrules[] indexes__________________|
260 Our data structures are more naturally iterated from bottom to top (inner
261 to outer dimensions). A previous version of this code actually worked
262 like that, but it didn't draw all of the vertical lines correctly as shown
263 above. It ended up rendering the above heading much like shown below,
264 which isn't what users expect. The "aaaa" label really needs to be shown
265 three times for clarity:
267 +-----------------------------------------------------+
269 +-----------------+-----------------+-----------------+
270 | bbbb1 | bbbb2 | bbbb3 |
271 +-----------------+-----------------+-----------------+
273 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
274 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|
275 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
277 bool *vrules = xzalloc (n_columns + 1);
278 vrules[0] = vrules[n_columns] = true;
279 for (int dim_index = a_axis->n_dimensions; --dim_index >= 0; )
281 const struct pivot_dimension *d = a_axis->dimensions[dim_index];
282 if (d->hide_all_labels)
285 for (int row_ofs = 0; row_ofs < d->label_depth; row_ofs++)
287 for (size_t x1 = 0; x1 < n_columns;)
289 const struct pivot_category *c = find_category (
290 d, dim_index, column_enumeration + x1 * stride,
291 d->label_depth - row_ofs - 1);
299 for (x2 = x1 + 1; x2 < n_columns; x2++)
303 const struct pivot_category *c2 = find_category (
304 d, dim_index, column_enumeration + x2 * stride,
305 d->label_depth - row_ofs - 1);
310 int y1 = top_row + row_ofs;
311 int y2 = top_row + row_ofs + c->extra_depth + 1;
312 bool is_outer_row = y1 == 0;
313 bool is_inner_row = y2 == b_size;
314 if (pivot_category_is_leaf (c) || c->show_label)
316 int bb[TABLE_N_AXES][2];
317 bb[a][0] = x1 + a_ofs;
318 bb[a][1] = x2 + a_ofs - 1;
321 bool rotate = ((rotate_inner_labels && is_inner_row)
322 || (rotate_outer_labels && is_outer_row));
323 fill_cell (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
324 label_style, label_style_idx, c->name, footnotes,
325 show_values, show_variables, rotate);
327 /* Draw all the vertical lines in our running example, other
328 than the far left and far right ones. Only the ones that
329 start in the last row of the heading are drawn with the
330 "category" style, the rest with the "dimension" style,
331 e.g. only the # below are category style:
333 +-----------------------------------------------------+
335 +-----------------+-----------------+-----------------+
336 | bbbb1 | bbbb2 | bbbb3 |
337 +-----------------+-----------------+-----------------+
338 | aaaa | aaaa | aaaa |
339 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
340 |aaaa1#aaaa2#aaaa3|aaaa1#aaaa2#aaaa3|aaaa1#aaaa2#aaaa3|
341 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
343 enum pivot_border style
344 = (y1 == b_size - 1 ? cat_col_vert : dim_col_vert);
347 draw_line (t, borders, style, b, x2 + a_ofs, y1,
353 draw_line (t, borders, style, b, x1 + a_ofs, y1,
359 /* Draws the horizontal lines within a dimension, that is, those
360 that separate a separating a category (or group) from its
361 parent group or dimension's label. Our running example
362 doesn't have groups but the ==== lines below show the
363 separators between categories and their dimension label:
365 +-----------------------------------------------------+
367 +=================+=================+=================+
368 | bbbb1 | bbbb2 | bbbb3 |
369 +-----------------+-----------------+-----------------+
370 | aaaa | aaaa | aaaa |
371 +=====+=====+=====+=====+=====+=====+=====+=====+=====+
372 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|
373 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
375 if (c->parent && c->parent->show_label)
376 draw_line (t, borders, cat_col_horz, a, y1,
377 x1 + a_ofs, x2 + a_ofs - 1);
382 if (d->root->show_label_in_corner && a_ofs > 0)
384 int bb[TABLE_N_AXES][2];
386 bb[a][1] = a_ofs - 1;
388 bb[b][1] = top_row + d->label_depth - 1;
389 fill_cell (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
390 corner_style, PIVOT_AREA_CORNER, d->root->name, footnotes,
391 show_values, show_variables, false);
394 /* Draw the horizontal line between dimensions, e.g. the ===== line here:
396 +-----------------------------------------------------+ __
398 +-----------------+-----------------+-----------------+ |dim "bbbb"
399 | bbbb1 | bbbb2 | bbbb3 | _|
400 +=================+=================+=================+ __
401 | aaaa | aaaa | aaaa | |
402 +-----+-----+-----+-----+-----+-----+-----+-----+-----+ |dim "aaaa"
403 |aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3|aaaa1|aaaa2|aaaa3| _|
404 +-----+-----+-----+-----+-----+-----+-----+-----+-----+
406 if (dim_index != a_axis->n_dimensions - 1)
407 draw_line (t, borders, dim_col_horz, a, top_row, a_ofs,
409 top_row += d->label_depth;
415 pivot_table_submit_layer (const struct pivot_table *pt,
416 const size_t *layer_indexes)
418 const size_t *pindexes[PIVOT_N_AXES]
419 = { [PIVOT_AXIS_LAYER] = layer_indexes };
421 size_t body[TABLE_N_AXES];
422 size_t *column_enumeration = pivot_table_enumerate_axis (
423 pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &body[H]);
424 size_t *row_enumeration = pivot_table_enumerate_axis (
425 pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &body[V]);
427 int stub[TABLE_N_AXES] = {
428 [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
429 [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
431 struct table *table = table_create (body[H] + stub[H],
433 stub[H], 0, stub[V], 0);
435 for (size_t i = 0; i < PIVOT_N_AREAS; i++)
436 table->styles[i] = table_area_style_override (
437 table->container, &pt->look->areas[i], NULL, NULL, false);
439 for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
441 const struct table_border_style *in = &pt->look->borders[i];
442 table->rule_colors[i] = pool_alloc (table->container,
443 sizeof *table->rule_colors[i]);
444 struct cell_color *out = table->rule_colors[i];
445 out->alpha = in->color.alpha;
446 out->r = in->color.r;
447 out->g = in->color.g;
448 out->b = in->color.b;
451 struct footnote **footnotes = XCALLOC (pt->n_footnotes, struct footnote *);
452 for (size_t i = 0; i < pt->n_footnotes; i++)
454 const struct pivot_footnote *pf = pt->footnotes[i];
459 char *content = pivot_value_to_string (pf->content, pt->show_values,
461 char *marker = pivot_value_to_string (pf->marker, pt->show_values,
463 footnotes[i] = table_create_footnote (
464 table, i, content, marker,
465 table_area_style_override (table->container,
466 &pt->look->areas[PIVOT_AREA_FOOTER],
467 pf->content->cell_style,
468 pf->content->font_style,
474 compose_headings (table,
475 &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
477 PIVOT_BORDER_DIM_COL_HORZ,
478 PIVOT_BORDER_DIM_COL_VERT,
479 PIVOT_BORDER_CAT_COL_HORZ,
480 PIVOT_BORDER_CAT_COL_VERT,
481 column_enumeration, body[H],
482 &pt->look->areas[PIVOT_AREA_COLUMN_LABELS],
483 PIVOT_AREA_COLUMN_LABELS,
484 &pt->look->areas[PIVOT_AREA_CORNER], footnotes,
485 pt->show_values, pt->show_variables,
486 pt->rotate_outer_row_labels, false);
488 compose_headings (table,
489 &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
491 PIVOT_BORDER_DIM_ROW_VERT,
492 PIVOT_BORDER_DIM_ROW_HORZ,
493 PIVOT_BORDER_CAT_ROW_VERT,
494 PIVOT_BORDER_CAT_ROW_HORZ,
495 row_enumeration, body[V],
496 &pt->look->areas[PIVOT_AREA_ROW_LABELS],
497 PIVOT_AREA_ROW_LABELS,
498 &pt->look->areas[PIVOT_AREA_CORNER], footnotes,
499 pt->show_values, pt->show_variables,
500 false, pt->rotate_inner_column_labels);
502 size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
504 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
505 &pt->axes[PIVOT_AXIS_ROW])
508 PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
510 &pt->axes[PIVOT_AXIS_COLUMN])
512 pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
513 const struct pivot_value *value = pivot_table_get (pt, dindexes);
515 x + stub[H], y + stub[V],
516 x + stub[H], y + stub[V],
517 &pt->look->areas[PIVOT_AREA_DATA], PIVOT_AREA_DATA,
519 pt->show_values, pt->show_variables, false);
528 if ((pt->corner_text || !pt->look->row_labels_in_corner)
529 && stub[H] && stub[V])
530 fill_cell (table, 0, 0, stub[H] - 1, stub[V] - 1,
531 &pt->look->areas[PIVOT_AREA_CORNER], PIVOT_AREA_CORNER,
532 pt->corner_text, footnotes,
533 pt->show_values, pt->show_variables, false);
535 if (table->n[H] && table->n[V])
538 table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_TOP),
539 0, table->n[H] - 1, 0);
541 table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_BOTTOM),
542 0, table->n[H] - 1, table->n[V]);
544 table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_LEFT),
545 0, 0, table->n[V] - 1);
547 table, get_table_rule (pt->look->borders, PIVOT_BORDER_INNER_RIGHT),
548 table->n[H], 0, table->n[V] - 1);
552 table, get_table_rule (pt->look->borders, PIVOT_BORDER_DATA_TOP),
553 0, table->n[H] - 1, stub[V]);
556 table, get_table_rule (pt->look->borders, PIVOT_BORDER_DATA_LEFT),
557 stub[H], 0, table->n[V] - 1);
560 free (column_enumeration);
561 free (row_enumeration);
563 struct table_item *ti = table_item_create (table, NULL, NULL, pt->notes);
565 if (pt->title && pt->show_title)
567 struct table_item_text *title = pivot_value_to_table_item_text (
568 pt->title, &pt->look->areas[PIVOT_AREA_TITLE], footnotes,
569 pt->show_values, pt->show_variables);
570 table_item_set_title (ti, title);
571 table_item_text_destroy (title);
574 const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
575 struct table_item_layers *layers = NULL;
576 for (size_t i = 0; i < layer_axis->n_dimensions; i++)
578 const struct pivot_dimension *d = layer_axis->dimensions[i];
583 layers = xzalloc (sizeof *layers);
584 layers->style = table_area_style_override (
585 NULL, &pt->look->areas[PIVOT_AREA_LAYERS], NULL, NULL, false);
586 layers->layers = xnmalloc (layer_axis->n_dimensions,
587 sizeof *layers->layers);
590 const struct pivot_value *name
591 = d->data_leaves[layer_indexes[i]]->name;
592 struct table_item_layer *layer = &layers->layers[layers->n_layers++];
593 struct string s = DS_EMPTY_INITIALIZER;
594 pivot_value_format_body (name, pt->show_values, pt->show_variables,
596 layer->content = ds_steal_cstr (&s);
597 layer->n_footnotes = 0;
598 layer->footnotes = xnmalloc (name->n_footnotes,
599 sizeof *layer->footnotes);
600 for (size_t i = 0; i < name->n_footnotes; i++)
602 struct footnote *f = footnotes[name->footnotes[i]->idx];
604 layer->footnotes[layer->n_footnotes++] = f;
610 table_item_set_layers (ti, layers);
611 table_item_layers_destroy (layers);
614 if (pt->caption && pt->show_caption)
616 struct table_item_text *caption = pivot_value_to_table_item_text (
617 pt->caption, &pt->look->areas[PIVOT_AREA_CAPTION], footnotes,
618 pt->show_values, pt->show_variables);
619 table_item_set_caption (ti, caption);
620 table_item_text_destroy (caption);
624 ti->pt = pivot_table_ref (pt);
626 table_item_submit (ti);
630 pivot_table_submit (struct pivot_table *pt)
632 pivot_table_assign_label_depth (CONST_CAST (struct pivot_table *, pt));
634 int old_decimal = settings_get_decimal_char (FMT_COMMA);
635 if (pt->decimal == '.' || pt->decimal == ',')
636 settings_set_decimal_char (pt->decimal);
638 if (pt->look->print_all_layers)
640 size_t *layer_indexes;
642 PIVOT_AXIS_FOR_EACH (layer_indexes, &pt->axes[PIVOT_AXIS_LAYER])
644 if (pt->look->paginate_layers)
645 page_eject_item_submit (page_eject_item_create ());
646 pivot_table_submit_layer (pt, layer_indexes);
650 pivot_table_submit_layer (pt, pt->current_layer);
652 settings_set_decimal_char (old_decimal);
654 pivot_table_unref (pt);