}
static void
-fill_cell (struct table *t, int x1, int y1, int x2, int y2,
+fill_cell (struct table *t, int x, int y,
int style_idx, const struct pivot_value *value,
bool rotate_label)
{
if (rotate_label)
options |= TABLE_CELL_ROTATE;
+ table_put (t, x, y, x, y, options, value);
+}
+
+static void
+fill_cell_spanned (struct table *t, int x1, int y1, int x2, int y2,
+ int style_idx, const struct pivot_value *value,
+ bool rotate_label, int options)
+{
+ options |= style_idx << TABLE_CELL_STYLE_SHIFT;
+ if (rotate_label)
+ options |= TABLE_CELL_ROTATE;
+
table_put (t, x1, y1, x2, y2, options, value);
}
enum pivot_border cat_col_vert,
const size_t *column_enumeration, size_t n_columns,
int label_style_idx,
- bool rotate_inner_labels, bool rotate_outer_labels)
+ bool rotate_inner_labels, bool rotate_outer_labels,
+ int area[TABLE_N_AXES][2])
{
const enum table_axis v = !h;
const int v_size = h_axis->label_depth;
- const int h_ofs = v_axis->label_depth;
+ const int h_ofs = v_axis->label_depth + area[h][0];
if (!h_axis->n_dimensions || !n_columns || !v_size)
return;
/* Below, we're going to iterate through the dimensions. Each dimension
occupies one or more rows in the heading. 'top_row' is the top row of
these (and 'top_row + d->label_depth - 1' is the bottom row). */
- int top_row = 0;
+ int top_row = area[v][0];
+
+ const int h_max = area[h][1];
+ const int v_max = area[v][1];
/* We're going to iterate through dimensions and the rows that label them
from top to bottom (from outer to inner dimensions). As we move downward,
int y1 = top_row + row_ofs;
int y2 = top_row + row_ofs + c->extra_depth + 1;
- bool is_outer_row = y1 == 0;
- bool is_inner_row = y2 == v_size;
if (pivot_category_is_leaf (c) || c->show_label)
{
int bb[TABLE_N_AXES][2];
bb[h][1] = x2 + h_ofs - 1;
bb[v][0] = y1;
bb[v][1] = y2 - 1;
+ bool is_outer_row = y1 == area[v][0];
+ bool is_inner_row = y2 == v_size + area[v][0];
bool rotate = ((rotate_inner_labels && is_inner_row)
|| (rotate_outer_labels && is_outer_row));
- fill_cell (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
- label_style_idx, c->name, rotate);
+ fill_cell_spanned (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
+ label_style_idx, c->name, rotate, 0);
/* Draw all the vertical lines in our running example, other
than the far left and far right ones. Only the ones that
= (y1 == v_size - 1 ? cat_col_vert : dim_col_vert);
if (!vrules[x2])
{
- draw_line (t, style, v, x2 + h_ofs, y1, t->n[v] - 1);
+ draw_line (t, style, v, x2 + h_ofs, y1, v_max);
vrules[x2] = true;
}
if (!vrules[x1])
{
- draw_line (t, style, v, x1 + h_ofs, y1, t->n[v] - 1);
+ draw_line (t, style, v, x1 + h_ofs, y1, v_max);
vrules[x1] = true;
}
}
}
}
- if (d->root->show_label_in_corner && h_ofs > 0)
+ if (d->root->show_label_in_corner && h_ofs > area[h][0])
{
int bb[TABLE_N_AXES][2];
bb[h][0] = 0;
bb[h][1] = h_ofs - 1;
bb[v][0] = top_row;
bb[v][1] = top_row + d->label_depth - 1;
- fill_cell (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
- PIVOT_AREA_CORNER, d->root->name, false);
+ fill_cell_spanned (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
+ PIVOT_AREA_CORNER, d->root->name, false, 0);
}
/* Draw the horizontal line between dimensions, e.g. the ===== line here:
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
*/
if (dim_index != h_axis->n_dimensions - 1)
- draw_line (t, dim_col_horz, h, top_row, h_ofs, t->n[h] - 1);
+ draw_line (t, dim_col_horz, h, top_row, h_ofs, h_max);
top_row += d->label_depth;
}
free (vrules);
return footnotes;
}
+static struct table *
+pivot_output_title (const struct pivot_table *pt)
+{
+ if (!pt->title || !pt->show_title)
+ return NULL;
+ struct table *title = create_aux_table (pt, 1, 1, PIVOT_AREA_TITLE);
+ fill_cell (title, 0, 0, PIVOT_AREA_TITLE, pt->title, false);
+ return title;
+}
+
+static int
+pivot_count_layers (const struct pivot_table *pt)
+{
+ const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
+ int n_layers = 0;
+ for (size_t i = 0; i < layer_axis->n_dimensions; i++)
+ {
+ const struct pivot_dimension *d = layer_axis->dimensions[i];
+ if (d->n_leaves)
+ n_layers++;
+ }
+ return n_layers;
+}
+
+static void
+put_layers (struct table *t, const struct pivot_table *pt,
+ const size_t *layer_indexes, int x1, int y1 UNUSED, int x2, int y2)
+{
+ assert (y1 + pivot_count_layers (pt) - 1 == y2);
+ const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
+ for (size_t i = 0; i < layer_axis->n_dimensions; i++)
+ {
+ const struct pivot_dimension *d = layer_axis->dimensions[i];
+ if (!d->n_leaves)
+ continue;
+
+ struct string s = DS_EMPTY_INITIALIZER;
+ pivot_value_format (d->data_leaves[layer_indexes[i]]->name, pt, &s);
+ int y = y2 - i;
+ fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_LAYERS, &s, false);
+ }
+}
+
+static struct table *
+pivot_output_layers (const struct pivot_table *pt, const size_t *layer_indexes)
+{
+ int n_layers = pivot_count_layers (pt);
+ if (!n_layers)
+ return NULL;
+
+ struct table *layers = create_aux_table (pt, 1, n_layers, PIVOT_AREA_LAYERS);
+ put_layers (layers, pt, layer_indexes, 0, 0, 0, n_layers - 1);
+ return layers;
+}
+
+static struct table *
+pivot_output_caption (const struct pivot_table *pt)
+{
+ if (!pt->caption || !pt->show_caption)
+ return NULL;
+
+ struct table *caption = create_aux_table (pt, 1, 1, PIVOT_AREA_CAPTION);
+ fill_cell (caption, 0, 0, PIVOT_AREA_CAPTION, pt->caption, false);
+ return caption;
+}
+
+static void
+put_footnotes (struct table *t, const struct pivot_table *pt,
+ struct pivot_footnote **f, size_t nf,
+ int x1, int x2, int y1)
+{
+ for (size_t i = 0; i < nf; i++)
+ {
+ struct string s = DS_EMPTY_INITIALIZER;
+ pivot_footnote_format_marker (f[i], pt, &s);
+ ds_put_cstr (&s, ". ");
+ pivot_value_format (f[i]->content, pt, &s);
+ int y = y1 + i;
+ fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_FOOTER, &s, false);
+ }
+}
+
+static struct table *
+pivot_output_footnotes (const struct pivot_table *pt,
+ struct pivot_footnote **f, size_t nf)
+{
+ if (!nf)
+ return NULL;
+
+ struct table *footnotes = create_aux_table (pt, 1, nf, PIVOT_AREA_FOOTER);
+ put_footnotes (footnotes, pt, f, nf, 0, 0, 0);
+ return footnotes;
+}
+
void
pivot_output (const struct pivot_table *pt,
const size_t *layer_indexes,
- bool printing UNUSED,
+ bool printing,
struct table **titlep,
struct table **layersp,
struct table **bodyp,
[H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
[V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
};
- struct table *body = table_create (data[H] + stub[H],
- data[V] + stub[V],
+
+ struct table *body = table_create (stub[H] + data[H], stub[V] + data[V],
stub[H], stub[V]);
for (size_t i = 0; i < PIVOT_N_AREAS; i++)
body->styles[i] = table_area_style_override (
: *src);
}
+ int body_area[TABLE_N_AXES][2] = {
+ [H] = { 0, body->n[H] - 1 },
+ [V] = { 0, body->n[V] - 1 },
+ };
compose_headings (body,
&pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
PIVOT_BORDER_DIM_COL_HORZ,
PIVOT_BORDER_CAT_COL_VERT,
column_enumeration, data[H],
PIVOT_AREA_COLUMN_LABELS,
- pt->rotate_outer_row_labels, false);
+ pt->rotate_outer_row_labels, false, body_area);
compose_headings (body,
&pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
PIVOT_BORDER_CAT_ROW_HORZ,
row_enumeration, data[V],
PIVOT_AREA_ROW_LABELS,
- false, pt->rotate_inner_column_labels);
+ false, pt->rotate_inner_column_labels, body_area);
size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
size_t y = 0;
{
pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
const struct pivot_value *value = pivot_table_get (pt, dindexes);
- fill_cell (body, x + stub[H], y + stub[V], x + stub[H], y + stub[V],
+ fill_cell (body, x + stub[H], y + stub[V],
PIVOT_AREA_DATA, value, false);
x++;
if ((pt->corner_text || !pt->look->row_labels_in_corner)
&& stub[H] && stub[V])
- fill_cell (body, 0, 0, stub[H] - 1, stub[V] - 1,
- PIVOT_AREA_CORNER, pt->corner_text, false);
+ fill_cell_spanned (body, 0, 0, stub[H] - 1, stub[V] - 1,
+ PIVOT_AREA_CORNER, pt->corner_text, false,
+ TABLE_CELL_FULL_WIDTH);
if (body->n[H] && body->n[V])
{
- table_hline (body, PIVOT_BORDER_INNER_TOP, 0, body->n[H] - 1, 0);
- table_hline (body, PIVOT_BORDER_INNER_BOTTOM, 0, body->n[H] - 1,
- body->n[V]);
- table_vline (body, PIVOT_BORDER_INNER_LEFT, 0, 0, body->n[V] - 1);
- table_vline (body, PIVOT_BORDER_INNER_RIGHT, body->n[H], 0,
- body->n[V] - 1);
+ int inner[TABLE_N_AXES][2] = {
+ [H] = { 0, body->n[H] },
+ [V] = { 0, body->n[V] },
+ };
+ table_hline (body, PIVOT_BORDER_INNER_TOP,
+ inner[H][0], inner[H][1] - 1, inner[V][0]);
+ table_hline (body, PIVOT_BORDER_INNER_BOTTOM,
+ inner[H][0], inner[H][1] - 1, inner[V][1]);
+ table_vline (body, PIVOT_BORDER_INNER_LEFT,
+ inner[H][0], inner[V][0], inner[V][1] - 1);
+ table_vline (body, PIVOT_BORDER_INNER_LEFT,
+ inner[H][1], inner[V][0], inner[V][1] - 1);
if (stub[V])
- table_hline (body, PIVOT_BORDER_DATA_TOP, 0, body->n[H] - 1, stub[V]);
+ table_hline (body, PIVOT_BORDER_DATA_TOP,
+ inner[H][0], inner[H][1] - 1, stub[V]);
if (stub[H])
- table_vline (body, PIVOT_BORDER_DATA_LEFT, stub[H], 0, body->n[V] - 1);
-
+ table_vline (body, PIVOT_BORDER_DATA_LEFT,
+ stub[H], inner[V][0], inner[V][1] - 1);
}
+
free (column_enumeration);
free (row_enumeration);
- /* Title. */
- struct table *title;
- if (pt->title && pt->show_title && titlep)
- {
- title = create_aux_table (pt, 1, 1, PIVOT_AREA_TITLE);
- fill_cell (title, 0, 0, 0, 0, PIVOT_AREA_TITLE, pt->title, false);
- }
- else
- title = NULL;
-
- /* Layers. */
- const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
- int n_layers = 0;
+ *bodyp = body;
+ if (titlep)
+ *titlep = pivot_output_title (pt);
if (layersp)
- for (size_t i = 0; i < layer_axis->n_dimensions; i++)
- {
- const struct pivot_dimension *d = layer_axis->dimensions[i];
- if (d->n_leaves)
- n_layers++;
- }
+ *layersp = pivot_output_layers (pt, layer_indexes);
+ if (captionp)
+ *captionp = pivot_output_caption (pt);
- struct table *layers;
- if (n_layers > 0)
+ if (fp || footnotesp)
{
- layers = create_aux_table (pt, 1, n_layers, PIVOT_AREA_LAYERS);
- size_t y = n_layers - 1;
- for (size_t i = 0; i < layer_axis->n_dimensions; i++)
+ size_t nf;
+ struct pivot_footnote **f = collect_footnotes (
+ pt, titlep ? *titlep : NULL, layersp ? *layersp : NULL, body,
+ captionp ? *captionp : NULL, &nf);
+
+ if (footnotesp)
+ *footnotesp = pivot_output_footnotes (pt, f, nf);
+
+ if (fp)
{
- const struct pivot_dimension *d = layer_axis->dimensions[i];
- if (!d->n_leaves)
- continue;
-
- struct string s = DS_EMPTY_INITIALIZER;
- pivot_value_format (d->data_leaves[layer_indexes[i]]->name, pt, &s);
- fill_cell_owned (layers, 0, y, 0, y, PIVOT_AREA_LAYERS, &s, false);
- y--;
+ *fp = f;
+ *nfp = nf;
}
+ else
+ free (f);
}
- else
- layers = NULL;
+}
+
+struct table *
+pivot_output_monolithic (const struct pivot_table *pt,
+ const size_t *layer_indexes,
+ bool printing)
+{
+ /* Table structure:
+
+ #============ Frame ============#
+ # Title #
+ # Layers #
+ # +--- Body ------------------+ #
+ # | Stub | #
+ # | +--------------------+ #
+ # | | Data | #
+ # | | | #
+ # | | | #
+ # | | | #
+ # +------+--------------------+ #
+ # Caption #
+ # Footnotes #
+ #===============================#
+
+ The regions are the following size:
+
+ - Frame: Either 0 or 1 row or column wide, depending on whether the table
+ style has an outer border on each side. The frame cells are always
+ empty and marked as TABLE_CELL_PADDING.
+
+ - Title: Either 0 or 1 row high.
+
+ - Layers: Usually, one row for each layer dimension, but less if a layer
+ dimension is empty (has no value).
+
+ - Body and data: Variable (and can be empty).
+
+ - Caption: Either 0 or 1 row high.
+
+ - Footnotes: From zero rows up to the number of footnotes in PT (only
+ footnotes referenced in the other regions are shown).
+
+ The title, layers, caption, and footnote Region join all the body's
+ columns into single cells.
+ */
+
+ /* Size of data region. */
+ size_t data[TABLE_N_AXES];
+ size_t *column_enumeration = pivot_table_enumerate_axis (
+ pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &data[H]);
+ size_t *row_enumeration = pivot_table_enumerate_axis (
+ pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &data[V]);
- /* Caption. */
- struct table *caption;
- if (pt->caption && pt->show_caption && captionp)
+ /* Size of stub region. */
+ int stub[TABLE_N_AXES] = {
+ [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
+ [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
+ };
+
+ int n_title = pt->title && pt->show_title;
+ int n_layers = pivot_count_layers (pt);
+ int n_caption = pt->caption && pt->show_caption;
+ int max_footnotes = pt->n_footnotes;
+
+ int min_body_width = n_title || n_layers || n_caption;
+ int n[TABLE_N_AXES] = {
+ [H] = MAX (min_body_width, stub[H] + data[H]),
+ [V] = n_title + n_layers + stub[V] + data[V] + n_caption + max_footnotes,
+ };
+
+ struct table *t = table_create (n[H], n[V],
+ stub[H], n_title + n_layers + stub[V]);
+ for (size_t i = 0; i < PIVOT_N_AREAS; i++)
+ t->styles[i] = table_area_style_override (
+ t->container, &pt->look->areas[i], NULL, NULL, false);
+
+ t->n_borders = PIVOT_N_BORDERS;
+ t->borders = pool_nmalloc (t->container, PIVOT_N_BORDERS, sizeof *t->borders);
+ for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
{
- caption = create_aux_table (pt, 1, 1, PIVOT_AREA_CAPTION);
- fill_cell (caption, 0, 0, 0, 0, PIVOT_AREA_CAPTION, pt->caption, false);
+ const struct table_border_style *src = &pt->look->borders[i];
+ struct table_border_style *dst = &t->borders[i];
+ *dst = (!printing && pt->show_grid_lines && src->stroke == TABLE_STROKE_NONE
+ ? (struct table_border_style) { .stroke = TABLE_STROKE_DASHED,
+ .color = CELL_COLOR_BLACK }
+ : *src);
}
- else
- caption = NULL;
- /* Footnotes. */
- size_t nf;
- struct pivot_footnote **f = collect_footnotes (pt, title, layers, body,
- caption, &nf);
- struct table *footnotes;
- if (nf && footnotesp)
- {
- footnotes = create_aux_table (pt, 1, nf, PIVOT_AREA_FOOTER);
+ int body[TABLE_N_AXES][2] = {
+ [H] = { 0, n[H] - 1 },
+ [V] = {
+ n_title + n_layers,
+ n_title + n_layers + stub[V] + data[V] - 1
+ },
+ };
+
+ if (n_layers)
+ put_layers (t, pt, layer_indexes,
+ body[H][0], n_title,
+ body[H][1], n_title + n_layers - 1);
+
+ compose_headings (t,
+ &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
+ PIVOT_BORDER_DIM_COL_HORZ,
+ PIVOT_BORDER_DIM_COL_VERT,
+ PIVOT_BORDER_CAT_COL_HORZ,
+ PIVOT_BORDER_CAT_COL_VERT,
+ column_enumeration, data[H],
+ PIVOT_AREA_COLUMN_LABELS,
+ pt->rotate_outer_row_labels, false, body);
+
+ compose_headings (t,
+ &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
+ PIVOT_BORDER_DIM_ROW_VERT,
+ PIVOT_BORDER_DIM_ROW_HORZ,
+ PIVOT_BORDER_CAT_ROW_VERT,
+ PIVOT_BORDER_CAT_ROW_HORZ,
+ row_enumeration, data[V],
+ PIVOT_AREA_ROW_LABELS,
+ false, pt->rotate_inner_column_labels, body);
- for (size_t i = 0; i < nf; i++)
+ int data_ofs[TABLE_N_AXES] = {
+ [H] = body[H][0] + stub[H],
+ [V] = body[V][0] + stub[V],
+ };
+
+ const size_t *pindexes[PIVOT_N_AXES] = { [PIVOT_AXIS_LAYER] = layer_indexes };
+ size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
+ int y = data_ofs[V];
+ PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
+ &pt->axes[PIVOT_AXIS_ROW])
+ {
+ int x = data_ofs[H];
+ PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
+ column_enumeration,
+ &pt->axes[PIVOT_AXIS_COLUMN])
{
- struct string s = DS_EMPTY_INITIALIZER;
- pivot_footnote_format_marker (f[i], pt, &s);
- ds_put_cstr (&s, ". ");
- pivot_value_format (f[i]->content, pt, &s);
- fill_cell_owned (footnotes, 0, i, 0, i, PIVOT_AREA_FOOTER, &s,
- false);
+ pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
+ const struct pivot_value *value = pivot_table_get (pt, dindexes);
+ fill_cell (t, x, y, PIVOT_AREA_DATA, value, false);
+ x++;
}
+
+ y++;
}
- else
- footnotes = NULL;
+ free (dindexes);
- *titlep = title;
- if (layersp)
- *layersp = layers;
- *bodyp = body;
- if (captionp)
- *captionp = caption;
- if (footnotesp)
- *footnotesp = footnotes;
- if (fp)
+ if ((pt->corner_text || !pt->look->row_labels_in_corner)
+ && stub[H] && stub[V])
+ fill_cell_spanned (t, body[H][0], body[V][0],
+ body[H][0] + stub[H] - 1, body[V][0] + stub[V] - 1,
+ PIVOT_AREA_CORNER, pt->corner_text, false,
+ TABLE_CELL_FULL_WIDTH);
+
+ if (body[H][1] >= body[H][0] && body[V][1] >= body[V][0])
{
- *fp = f;
- *nfp = nf;
+ table_hline (t, PIVOT_BORDER_INNER_TOP,
+ body[H][0], body[H][1], body[V][0]);
+ table_hline (t, PIVOT_BORDER_INNER_BOTTOM,
+ body[H][0], body[H][1], body[V][1] + 1);
+ table_vline (t, PIVOT_BORDER_INNER_LEFT,
+ body[H][0], body[V][0], body[V][1]);
+ table_vline (t, PIVOT_BORDER_INNER_RIGHT,
+ body[H][1] + 1, body[V][0], body[V][1]);
+
+ if (stub[V])
+ table_hline (t, PIVOT_BORDER_DATA_TOP,
+ body[H][0], body[H][1], data_ofs[V]);
+ if (stub[H])
+ table_vline (t, PIVOT_BORDER_DATA_LEFT,
+ data_ofs[H], body[V][0], body[V][1]);
}
- else
- free (f);
+
+ if (n_caption)
+ fill_cell_spanned (t,
+ body[H][0], body[V][1] + 1,
+ body[H][1], body[V][1] + 1,
+ PIVOT_AREA_CAPTION, pt->caption, false,
+ TABLE_CELL_FULL_WIDTH);
+
+ size_t nf;
+ struct pivot_footnote **f = collect_footnotes (pt, t, NULL, NULL, NULL, &nf);
+ assert (nf <= max_footnotes);
+ put_footnotes (t, pt, f, nf, body[H][0], body[H][1],
+ body[V][1] + 1 + n_caption);
+ t->n[V] = body[V][1] + 1 + n_caption + nf;
+ free (f);
+
+ if (n_title)
+ {
+#if 0
+ printf ("%d,%d - %d,%d\n",
+ body[H][0], 0,
+ body[H][1], 0);
+#endif
+ fill_cell_spanned (t,
+ body[H][0], 0,
+ body[H][1], 0,
+ PIVOT_AREA_TITLE, pt->title, false,
+ TABLE_CELL_FULL_WIDTH);
+ }
+
+ free (column_enumeration);
+ free (row_enumeration);
+
+#if 0
+ printf ("output page:\n");
+ for (int y = 0; y < t->n[V]; y++)
+ for (int x = 0; x < t->n[H]; x++)
+ {
+ struct table_cell cell;
+
+ table_get_cell (t, x, y, &cell);
+ char *s = pivot_value_to_string (cell.value, NULL);
+ printf ("%d,%d - %d,%d: %s\n",
+ cell.d[H][0], cell.d[V][0],
+ cell.d[H][1], cell.d[V][1],
+ s);
+ free (s);
+ }
+#endif
+
+ return t;
}
void