X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fpivot-output.c;h=e50c273faf1421ebde2b4556244a5909f16abbcf;hb=8502b942ecdbc7d5a688f695cebc6f1bfac79868;hp=2d4af6b3b21fa43b5ad2e69d8a9a007f44a110ba;hpb=11f756eeedc92ee354345940fa621737886a75da;p=pspp diff --git a/src/output/pivot-output.c b/src/output/pivot-output.c index 2d4af6b3b2..e50c273faf 100644 --- a/src/output/pivot-output.c +++ b/src/output/pivot-output.c @@ -112,7 +112,7 @@ table_area_style_override (struct pool *pool, } 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) { @@ -120,6 +120,18 @@ fill_cell (struct table *t, int x1, int y1, int x2, int y2, 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); } @@ -162,11 +174,12 @@ compose_headings (struct table *t, 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; @@ -176,7 +189,10 @@ compose_headings (struct table *t, /* 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, @@ -258,8 +274,6 @@ compose_headings (struct table *t, 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]; @@ -267,10 +281,12 @@ compose_headings (struct table *t, 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 @@ -292,12 +308,12 @@ compose_headings (struct table *t, = (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; } } @@ -324,15 +340,15 @@ compose_headings (struct table *t, } } - 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: @@ -348,7 +364,7 @@ compose_headings (struct table *t, +-----+-----+-----+-----+-----+-----+-----+-----+-----+ */ 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); @@ -432,10 +448,104 @@ collect_footnotes (const struct pivot_table *pt, 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, @@ -456,8 +566,8 @@ pivot_output (const struct pivot_table *pt, [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 ( @@ -476,6 +586,10 @@ pivot_output (const struct pivot_table *pt, : *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, @@ -484,7 +598,7 @@ pivot_output (const struct pivot_table *pt, 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], @@ -494,7 +608,7 @@ pivot_output (const struct pivot_table *pt, 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; @@ -508,7 +622,7 @@ pivot_output (const struct pivot_table *pt, { 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++; @@ -520,115 +634,285 @@ pivot_output (const struct pivot_table *pt, 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