X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fpivot-table.c;h=f2967eae81de9d14ac60dd015b9421112b2c2068;hb=52afd109c4f1e4a524ff068c0389388592055455;hp=674fe2cdf5c6b80babb44bd7619fb866eb1420ff;hpb=7163d44c60105d8a50b4888caed804338eee8b43;p=pspp diff --git a/src/output/pivot-table.c b/src/output/pivot-table.c index 674fe2cdf5..f2967eae81 100644 --- a/src/output/pivot-table.c +++ b/src/output/pivot-table.c @@ -26,6 +26,7 @@ #include "data/variable.h" #include "libpspp/hash-functions.h" #include "libpspp/i18n.h" +#include "output/driver.h" #include "gl/c-ctype.h" #include "gl/intprops.h" @@ -61,6 +62,70 @@ pivot_area_to_string (enum pivot_area area) } } +const struct area_style * +pivot_area_get_default_style (enum pivot_area area) +{ +#define STYLE(BOLD, H, V, L, R, T, B) { \ + .cell_style = { \ + .halign = TABLE_HALIGN_##H, \ + .valign = TABLE_VALIGN_##V, \ + .margin = { [TABLE_HORZ][0] = L, [TABLE_HORZ][1] = R, \ + [TABLE_VERT][0] = T, [TABLE_VERT][1] = B }, \ + }, \ + .font_style = { \ + .bold = BOLD, \ + .fg = { [0] = CELL_COLOR_BLACK, [1] = CELL_COLOR_BLACK}, \ + .bg = { [0] = CELL_COLOR_WHITE, [1] = CELL_COLOR_WHITE}, \ + .size = 9, \ + .typeface = (char *) "Sans Serif", \ + }, \ + } + static const struct area_style default_area_styles[PIVOT_N_AREAS] = { + [PIVOT_AREA_TITLE] = STYLE(true, CENTER, CENTER, 8,11,1,8), + [PIVOT_AREA_CAPTION] = STYLE(false, LEFT, TOP, 8,11,1,1), + [PIVOT_AREA_FOOTER] = STYLE(false, LEFT, TOP, 11, 8,2,3), + [PIVOT_AREA_CORNER] = STYLE(false, LEFT, BOTTOM, 8,11,1,1), + [PIVOT_AREA_COLUMN_LABELS] = STYLE(false, CENTER, BOTTOM, 8,11,1,3), + [PIVOT_AREA_ROW_LABELS] = STYLE(false, LEFT, TOP, 8,11,1,3), + [PIVOT_AREA_DATA] = STYLE(false, MIXED, TOP, 8,11,1,1), + [PIVOT_AREA_LAYERS] = STYLE(false, LEFT, BOTTOM, 8,11,1,3), + }; +#undef STYLE + + return &default_area_styles[area]; +} + +void +pivot_border_get_default_style (enum pivot_border border, + struct table_border_style *style) +{ + static const enum table_stroke default_strokes[PIVOT_N_BORDERS] = { + [PIVOT_BORDER_TITLE] = TABLE_STROKE_NONE, + [PIVOT_BORDER_OUTER_LEFT] = TABLE_STROKE_NONE, + [PIVOT_BORDER_OUTER_TOP] = TABLE_STROKE_NONE, + [PIVOT_BORDER_OUTER_RIGHT] = TABLE_STROKE_NONE, + [PIVOT_BORDER_OUTER_BOTTOM] = TABLE_STROKE_NONE, + [PIVOT_BORDER_INNER_LEFT] = TABLE_STROKE_THICK, + [PIVOT_BORDER_INNER_TOP] = TABLE_STROKE_THICK, + [PIVOT_BORDER_INNER_RIGHT] = TABLE_STROKE_THICK, + [PIVOT_BORDER_INNER_BOTTOM] = TABLE_STROKE_THICK, + [PIVOT_BORDER_DATA_LEFT] = TABLE_STROKE_THICK, + [PIVOT_BORDER_DATA_TOP] = TABLE_STROKE_THICK, + [PIVOT_BORDER_DIM_ROW_HORZ] = TABLE_STROKE_SOLID, + [PIVOT_BORDER_DIM_ROW_VERT] = TABLE_STROKE_NONE, + [PIVOT_BORDER_DIM_COL_HORZ] = TABLE_STROKE_SOLID, + [PIVOT_BORDER_DIM_COL_VERT] = TABLE_STROKE_SOLID, + [PIVOT_BORDER_CAT_ROW_HORZ] = TABLE_STROKE_NONE, + [PIVOT_BORDER_CAT_ROW_VERT] = TABLE_STROKE_NONE, + [PIVOT_BORDER_CAT_COL_HORZ] = TABLE_STROKE_SOLID, + [PIVOT_BORDER_CAT_COL_VERT] = TABLE_STROKE_SOLID, + }; + *style = (struct table_border_style) { + .stroke = default_strokes[border], + .color = CELL_COLOR_BLACK, + }; +} + /* Returns the name of BORDER. */ const char * pivot_border_to_string (enum pivot_border border) @@ -279,7 +344,14 @@ pivot_dimension_create__ (struct pivot_table *table, axis->dimensions, (axis->n_dimensions + 1) * sizeof *axis->dimensions); axis->dimensions[axis->n_dimensions++] = d; - /* XXX extent and label_depth need to be calculated later. */ + if (axis_type == PIVOT_AXIS_LAYER) + { + free (table->current_layer); + table->current_layer = xcalloc (axis[PIVOT_AXIS_LAYER].n_dimensions, + sizeof *table->current_layer); + } + + /* axis->extent and axis->label_depth will be calculated later. */ return d; } @@ -293,6 +365,7 @@ pivot_dimension_destroy (struct pivot_dimension *d) pivot_category_destroy (d->root); free (d->data_leaves); free (d->presentation_leaves); + free (d); } /* Returns the first leaf node in an in-order traversal that is a child of @@ -505,6 +578,7 @@ pivot_category_destroy (struct pivot_category *c) pivot_value_destroy (c->name); for (size_t i = 0; i < c->n_subs; i++) pivot_category_destroy (c->subs[i]); + free (c->subs); free (c); } @@ -578,19 +652,12 @@ pivot_result_class_change (const char *s_, const struct fmt_spec *format) return rc != NULL; } -/* One piece of data within a pivot table. */ -struct pivot_cell - { - struct hmap_node hmap_node; /* In struct pivot_table's 'cells' hmap. */ - struct pivot_value *value; - unsigned int idx[]; /* One index per table dimension. */ - }; - /* Pivot tables. */ /* Creates and returns a new pivot table with the given TITLE. TITLE should be a text string marked for translation but not actually translated yet, - e.g. N_("Descriptive Statistics"). + e.g. N_("Descriptive Statistics"). The un-translated text string is used as + the pivot table's subtype. Operations commonly performed on the new pivot_table: @@ -602,17 +669,22 @@ struct pivot_cell This function is a shortcut for pivot_table_create__() for the most common case. Use pivot_table_create__() directly if the title should be some kind - of value other than an ordinary text string. + of value other than an ordinary text string, or if the subtype should be +different from the title. See the large comment at the top of pivot-table.h for general advice on creating pivot tables. */ struct pivot_table * pivot_table_create (const char *title) { - return pivot_table_create__ (pivot_value_new_text (title)); + return pivot_table_create__ (pivot_value_new_text (title), title); } -/* Creates and returns a new pivot table with the given TITLE. +/* Creates and returns a new pivot table with the given TITLE, and takes + ownership of TITLE. The new pivot table's subtype is SUBTYPE, which + should be an untranslated English string that describes the contents of + the table at a high level without being specific about the variables or + other context involved. Operations commonly performed on the new pivot_table: @@ -625,39 +697,23 @@ pivot_table_create (const char *title) See the large comment at the top of pivot-table.h for general advice on creating pivot tables. */ struct pivot_table * -pivot_table_create__ (struct pivot_value *title) +pivot_table_create__ (struct pivot_value *title, const char *subtype) { struct pivot_table *table = xzalloc (sizeof *table); + table->ref_cnt = 1; + table->show_caption = true; table->weight_format = (struct fmt_spec) { FMT_F, 40, 0 }; table->title = title; + table->subtype = subtype ? pivot_value_new_text (subtype) : NULL; + table->command_c = output_get_command_name (); + + table->sizing[TABLE_HORZ].range[0] = 50; + table->sizing[TABLE_HORZ].range[1] = 72; + table->sizing[TABLE_VERT].range[0] = 36; + table->sizing[TABLE_VERT].range[1] = 120; - /* Set default area styles. */ -#define STYLE(BOLD, H, V, L, R, T, B) { \ - .cell_style = { \ - .halign = TABLE_HALIGN_##H, \ - .valign = TABLE_VALIGN_##V, \ - .margin = { [TABLE_HORZ][0] = L, [TABLE_HORZ][1] = R, \ - [TABLE_VERT][0] = T, [TABLE_VERT][1] = B }, \ - }, \ - .font_style = { \ - .bold = BOLD, \ - .fg = { [0] = CELL_COLOR_BLACK, [1] = CELL_COLOR_BLACK}, \ - .bg = { [0] = CELL_COLOR_WHITE, [1] = CELL_COLOR_WHITE}, \ - }, \ - } - static const struct area_style default_area_styles[PIVOT_N_AREAS] = { - [PIVOT_AREA_TITLE] = STYLE( true, CENTER, CENTER, 8,11,1,8), - [PIVOT_AREA_CAPTION] = STYLE(false, LEFT, TOP, 8,11,1,1), - [PIVOT_AREA_FOOTER] = STYLE(false, LEFT, TOP, 11, 8,2,3), - [PIVOT_AREA_CORNER] = STYLE(false, LEFT, BOTTOM, 8,11,1,1), - [PIVOT_AREA_COLUMN_LABELS] = STYLE(false, CENTER, BOTTOM, 8,11,1,3), - [PIVOT_AREA_ROW_LABELS] = STYLE(false, LEFT, TOP, 8,11,1,3), - [PIVOT_AREA_DATA] = STYLE(false, MIXED, TOP, 8,11,1,1), - [PIVOT_AREA_LAYERS] = STYLE(false, LEFT, BOTTOM, 8,11,1,3), - }; -#undef STYLE for (size_t i = 0; i < PIVOT_N_AREAS; i++) - table->areas[i] = default_area_styles[i]; + area_style_copy (NULL, &table->areas[i], pivot_area_get_default_style (i)); /* Set default border styles. */ static const enum table_stroke default_strokes[PIVOT_N_BORDERS] = { @@ -701,7 +757,7 @@ struct pivot_table * pivot_table_create_for_text (struct pivot_value *title, struct pivot_value *content) { - struct pivot_table *table = pivot_table_create__ (title); + struct pivot_table *table = pivot_table_create__ (title, "Error"); struct pivot_dimension *d = pivot_dimension_create ( table, PIVOT_AXIS_ROW, N_("Error")); @@ -713,12 +769,27 @@ pivot_table_create_for_text (struct pivot_value *title, return table; } -/* Destroys TABLE and frees everything it points to. */ +/* Increases TABLE's reference count, indicating that it has an additional + owner. A pivot table that is shared among multiple owners must not be + modified. */ +struct pivot_table * +pivot_table_ref (const struct pivot_table *table_) +{ + struct pivot_table *table = CONST_CAST (struct pivot_table *, table_); + table->ref_cnt++; + return table; +} + +/* Decreases TABLE's reference count, indicating that it has one fewer owner. + If TABLE no longer has any owners, it is freed. */ void -pivot_table_destroy (struct pivot_table *table) +pivot_table_unref (struct pivot_table *table) { if (!table) return; + assert (table->ref_cnt > 0); + if (--table->ref_cnt) + return; free (table->current_layer); free (table->table_look); @@ -771,6 +842,14 @@ pivot_table_destroy (struct pivot_table *table) free (table); } +/* Returns true if TABLE has more than one owner. A pivot table that is shared + among multiple owners must not be modified. */ +bool +pivot_table_is_shared (const struct pivot_table *table) +{ + return table->ref_cnt > 1; +} + /* Sets the format used for PIVOT_RC_COUNT cells to the one used for variable WV, which should be the weight variable for the dictionary whose data or statistics are being put into TABLE. @@ -957,9 +1036,9 @@ pivot_make_default_footnote_marker (int idx, bool show_numeric_markers) return pivot_value_new_user_text (text, -1); } -/* Creates or modifies a footnote in TABLE with 0-based number IDX. If MARKER - is nonnull, sets the footnote's marker; if CONTENT is nonnull, sets the - footnote's content. */ +/* Creates or modifies a footnote in TABLE with 0-based number IDX (and creates + all lower indexes as a side effect). If MARKER is nonnull, sets the + footnote's marker; if CONTENT is nonnull, sets the footnote's content. */ struct pivot_footnote * pivot_table_create_footnote__ (struct pivot_table *table, size_t idx, struct pivot_value *marker, @@ -978,6 +1057,7 @@ pivot_table_create_footnote__ (struct pivot_table *table, size_t idx, f->marker = pivot_make_default_footnote_marker ( f->idx, table->show_numeric_markers); f->content = NULL; + f->show = true; table->footnotes[table->n_footnotes++] = f; } @@ -1058,7 +1138,7 @@ pivot_table_enumerate_axis (const struct pivot_table *table, axis->n_dimensions), 1), sizeof *enumeration); size_t *p = enumeration; - size_t *dindexes = xcalloc (table->n_dimensions, sizeof *dindexes); + size_t *dindexes = XCALLOC (table->n_dimensions, size_t); size_t *axis_indexes; PIVOT_AXIS_FOR_EACH (axis_indexes, axis) @@ -1342,6 +1422,52 @@ compose_headings (const struct pivot_axis *axis, return headings; } +static void +free_headings (const struct pivot_axis *axis, char ***headings) +{ + for (size_t i = 0; i < axis->label_depth; i++) + { + for (size_t j = 0; j < axis->extent; j++) + free (headings[i][j]); + free (headings[i]); + } + free (headings); +} + +static void +pivot_table_sizing_dump (const char *name, const struct pivot_table_sizing *s, + int indentation) +{ + indent (indentation); + printf ("%ss: min=%d, max=%d\n", name, s->range[0], s->range[1]); + if (s->n_widths) + { + indent (indentation + 1); + printf ("%s widths:", name); + for (size_t i = 0; i < s->n_widths; i++) + printf (" %d", s->widths[i]); + printf ("\n"); + } + if (s->n_breaks) + { + indent (indentation + 1); + printf ("break after %ss:", name); + for (size_t i = 0; i < s->n_breaks; i++) + printf (" %zu", s->breaks[i]); + printf ("\n"); + } + if (s->n_keeps) + { + indent (indentation + 1); + printf ("keep %ss together:", name); + for (size_t i = 0; i < s->n_keeps; i++) + printf (" [%zu,%zu]", + s->keeps[i].ofs, + s->keeps[i].ofs + s->keeps[i].n - 1); + printf ("\n"); + } +} + void pivot_table_dump (const struct pivot_table *table, int indentation) { @@ -1361,9 +1487,20 @@ pivot_table_dump (const struct pivot_table *table, int indentation) if (table->date) { indent (indentation); - printf ("date: %s", ctime (&table->date)); /* XXX thread unsafe */ + + struct tm *tm = localtime (&table->date); + printf ("date: %d-%02d-%02d %d:%02d:%02d\n", tm->tm_year + 1900, + tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec); } + indent (indentation); + printf ("sizing:\n"); + pivot_table_sizing_dump ("column", &table->sizing[TABLE_HORZ], + indentation + 1); + pivot_table_sizing_dump ("row", &table->sizing[TABLE_VERT], + indentation + 1); + indent (indentation); printf ("areas:\n"); for (enum pivot_area area = 0; area < PIVOT_N_AREAS; area++) @@ -1378,7 +1515,7 @@ pivot_table_dump (const struct pivot_table *table, int indentation) pivot_dimension_dump (table->dimensions[i], indentation); /* Presentation and data indexes. */ - size_t *dindexes = xcalloc (table->n_dimensions, sizeof *dindexes); + size_t *dindexes = XCALLOC (table->n_dimensions, size_t); const struct pivot_axis *layer_axis = &table->axes[PIVOT_AXIS_LAYER]; if (layer_axis->n_dimensions) @@ -1420,7 +1557,7 @@ pivot_table_dump (const struct pivot_table *table, int indentation) fputs (" =", stdout); struct pivot_value **names = xnmalloc (layer_axis->label_depth, - sizeof *names); + sizeof *names); size_t n_names = 0; for (const struct pivot_category *c = d->presentation_leaves[layer_indexes[i]]; @@ -1431,7 +1568,7 @@ pivot_table_dump (const struct pivot_table *table, int indentation) names[n_names++] = c->name; } - for (size_t i = n_names; i-- > 0; ) + for (size_t i = n_names; i-- > 0;) { putchar (' '); pivot_value_dump (names[i]); @@ -1460,6 +1597,7 @@ pivot_table_dump (const struct pivot_table *table, int indentation) } putchar ('\n'); } + free_headings (&table->axes[PIVOT_AXIS_COLUMN], column_headings); indent (indentation + 1); printf ("-----------------------------------------------\n"); @@ -1508,6 +1646,7 @@ pivot_table_dump (const struct pivot_table *table, int indentation) free (column_enumeration); free (row_enumeration); + free_headings (&table->axes[PIVOT_AXIS_ROW], row_headings); } pivot_table_dump_value (table->caption, "caption", indentation); @@ -1526,6 +1665,7 @@ pivot_table_dump (const struct pivot_table *table, int indentation) putchar ('\n'); } + free (dindexes); settings_set_decimal_char (old_decimal); } @@ -1733,7 +1873,7 @@ pivot_value_format_body (const struct pivot_value *value, break; case PIVOT_VALUE_TEMPLATE: - pivot_format_template (out, value->template.s, value->template.args, + pivot_format_template (out, value->template.local, value->template.args, value->template.n_args, show_values, show_variables); break; @@ -1752,10 +1892,13 @@ pivot_value_format (const struct pivot_value *value, enum settings_value_show show_variables, struct string *out) { - pivot_value_format_body ( value, show_values, show_variables, out); + pivot_value_format_body (value, show_values, show_variables, out); - if (value->subscript) - ds_put_format (out, "_%s", value->subscript); + if (value->n_subscripts) + { + for (size_t i = 0; i < value->n_subscripts; i++) + ds_put_format (out, "%c%s", i ? ',' : '_', value->subscripts[i]); + } if (value->superscript) ds_put_format (out, "^%s", value->superscript); @@ -1789,10 +1932,16 @@ pivot_value_destroy (struct pivot_value *value) font_style_uninit (value->font_style); free (value->font_style); free (value->cell_style); - for (size_t i = 0; i < value->n_footnotes; i++) - pivot_footnote_destroy (value->footnotes[i]); + /* Do not free the elements of footnotes because VALUE does not own + them. */ free (value->footnotes); + for (size_t i = 0; i < value->n_subscripts; i++) + free (value->subscripts[i]); + free (value->subscripts); + + free (value->superscript); + switch (value->type) { case PIVOT_VALUE_NUMERIC: @@ -1800,7 +1949,8 @@ pivot_value_destroy (struct pivot_value *value) free (value->numeric.value_label); break; - case SETTINGS_VALUE_SHOW_VALUE: + case PIVOT_VALUE_STRING: + free (value->string.s); free (value->string.var_name); free (value->string.value_label); break; @@ -1820,7 +1970,9 @@ pivot_value_destroy (struct pivot_value *value) break; case PIVOT_VALUE_TEMPLATE: - free (value->template.s); + free (value->template.local); + if (value->template.id != value->template.local) + free (value->template.id); for (size_t i = 0; i < value->template.n_args; i++) pivot_argument_uninit (&value->template.args[i]); free (value->template.args); @@ -1834,15 +1986,16 @@ pivot_value_destroy (struct pivot_value *value) DEFAULT_STYLE for the parts of the style that VALUE doesn't override. */ void pivot_value_get_style (struct pivot_value *value, - const struct area_style *default_style, + const struct font_style *base_font_style, + const struct cell_style *base_cell_style, struct area_style *area) { - font_style_copy (&area->font_style, (value->font_style - ? value->font_style - : &default_style->font_style)); - area->cell_style = (value->cell_style - ? *value->cell_style - : default_style->cell_style); + font_style_copy (NULL, &area->font_style, (value->font_style + ? value->font_style + : base_font_style)); + area->cell_style = *(value->cell_style + ? value->cell_style + : base_cell_style); } /* Copies AREA into VALUE's style. */ @@ -1854,7 +2007,7 @@ pivot_value_set_style (struct pivot_value *value, font_style_uninit (value->font_style); else value->font_style = xmalloc (sizeof *value->font_style); - font_style_copy (value->font_style, &area->font_style); + font_style_copy (NULL, value->font_style, &area->font_style); if (!value->cell_style) value->cell_style = xmalloc (sizeof *value->cell_style); @@ -1873,6 +2026,14 @@ pivot_argument_uninit (struct pivot_argument *arg) } } +/* Creates and returns a new pivot_value whose contents is the null-terminated + string TEXT. Takes ownership of TEXT. + + This function is for text strings provided by the user (with the exception + that pivot_value_new_variable() should be used for variable names). For + strings that are part of the PSPP user interface, such as names of + procedures, statistics, annotations, error messages, etc., use + pivot_value_new_text(). */ struct pivot_value * pivot_value_new_user_text_nocopy (char *text) { @@ -1889,6 +2050,17 @@ pivot_value_new_user_text_nocopy (char *text) return value; } +/* Creates and returns a new pivot_value whose contents is the LENGTH bytes of + TEXT. Use SIZE_MAX if TEXT is null-teriminated and its length is not known + in advance. + + This function is for text strings provided by the user (with the exception + that pivot_value_new_variable() should be used for variable names). For + strings that are part of the PSPP user interface, such as names of + procedures, statistics, annotations, error messages, etc., use + pivot_value_new_text().j + + The caller retains ownership of TEXT.*/ struct pivot_value * pivot_value_new_user_text (const char *text, size_t length) { @@ -1896,8 +2068,12 @@ pivot_value_new_user_text (const char *text, size_t length) xmemdup0 (text, length != SIZE_MAX ? length : strlen (text))); } -/* TEXT should be a translatable string, but not actually translated yet, - e.g. enclosed in N_(). */ +/* Creates and returns new pivot_value whose contents is TEXT, which should be + a translatable string, but not actually translated yet, e.g. enclosed in + N_(). This function is for text strings that are part of the PSPP user + interface, such as names of procedures, statistics, annotations, error + messages, etc. For strings that come from the user, use + pivot_value_new_user_text(). */ struct pivot_value * pivot_value_new_text (const char *text) { @@ -1917,8 +2093,8 @@ pivot_value_new_text (const char *text) return value; } -/* FORMAT should be a translatable string, but not actually translated yet, - e.g. enclosed in N_(). */ +/* Same as pivot_value_new_text() but its argument is a printf()-like format + string. */ struct pivot_value * PRINTF_FORMAT (1, 2) pivot_value_new_text_format (const char *format, ...) { @@ -2014,8 +2190,7 @@ pivot_value_new_value (const union value *value, int width, struct pivot_value *pv = xzalloc (sizeof *pv); if (width > 0) { - char *s = recode_string (UTF8, encoding, - CHAR_CAST (char *, value_str (value, width)), + char *s = recode_string (UTF8, encoding, CHAR_CAST (char *, value->s), width); size_t n = strlen (s); while (n > 0 && s[n - 1] == ' ') @@ -2053,8 +2228,14 @@ pivot_value_new_variable (const struct variable *variable) /* Attaches a reference to FOOTNOTE to V. */ void pivot_value_add_footnote (struct pivot_value *v, - struct pivot_footnote *footnote) + const struct pivot_footnote *footnote) { + /* Some legacy tables include numerous duplicate footnotes. Suppress + them. */ + for (size_t i = 0; i < v->n_footnotes; i++) + if (v->footnotes[i] == footnote) + return; + v->footnotes = xrealloc (v->footnotes, (v->n_footnotes + 1) * sizeof *v->footnotes); v->footnotes[v->n_footnotes++] = footnote; @@ -2063,7 +2244,7 @@ pivot_value_add_footnote (struct pivot_value *v, /* If VALUE is a numeric value, and RC is a result class such as PIVOT_RC_COUNT, changes VALUE's format to the result class's. */ void -pivot_value_set_rc (struct pivot_table *table, struct pivot_value *value, +pivot_value_set_rc (const struct pivot_table *table, struct pivot_value *value, const char *rc) { if (value->type == PIVOT_VALUE_NUMERIC)