X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Ftable.c;h=3d07eb40d47563b27c059ee36d0473ce94d85c47;hb=dfb794fa53a423c1f20c3a21811c0bec4a64a916;hp=ace6c3cb59df74396fca4b2e2ac5cf06cb18ca8f;hpb=b11b5d8747643a2d6f81416ed584d3de4e276990;p=pspp diff --git a/src/output/table.c b/src/output/table.c index ace6c3cb59..3d07eb40d4 100644 --- a/src/output/table.c +++ b/src/output/table.c @@ -29,9 +29,9 @@ #include "libpspp/compiler.h" #include "libpspp/pool.h" #include "libpspp/str.h" -#include "output/table-item.h" +#include "output/output-item.h" +#include "output/pivot-table.h" #include "output/table.h" -#include "output/text-item.h" #include "gl/xalloc.h" @@ -90,89 +90,6 @@ table_area_style_free (struct table_area_style *style) free (style); } } - -void -table_cell_format_footnote_markers (const struct table_cell *cell, - struct string *s) -{ - for (size_t i = 0; i < cell->n_footnotes; i++) - { - if (i) - ds_put_byte (s, ','); - ds_put_cstr (s, cell->footnotes[i]->marker); - } -} - -static const struct footnote ** -add_footnotes (const struct footnote **refs, size_t n_refs, - const struct footnote **footnotes, size_t *allocated, size_t *n) -{ - for (size_t i = 0; i < n_refs; i++) - { - const struct footnote *f = refs[i]; - if (f->idx >= *allocated) - { - size_t new_allocated = (f->idx + 1) * 2; - footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes); - while (*allocated < new_allocated) - footnotes[(*allocated)++] = NULL; - } - footnotes[f->idx] = f; - if (f->idx >= *n) - *n = f->idx + 1; - } - return footnotes; -} - -size_t -table_collect_footnotes (const struct table_item *item, - const struct footnote ***footnotesp) -{ - const struct footnote **footnotes = NULL; - size_t allocated = 0; - size_t n = 0; - - struct table *t = item->table; - for (int y = 0; y < t->n[V]; y++) - { - struct table_cell cell; - for (int x = 0; x < t->n[H]; x = cell.d[TABLE_HORZ][1]) - { - table_get_cell (t, x, y, &cell); - - if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0]) - footnotes = add_footnotes (cell.footnotes, cell.n_footnotes, - footnotes, &allocated, &n); - } - } - - const struct table_item_text *title = table_item_get_title (item); - if (title) - footnotes = add_footnotes (title->footnotes, title->n_footnotes, - footnotes, &allocated, &n); - - const struct table_item_layers *layers = table_item_get_layers (item); - if (layers) - { - for (size_t i = 0; i < layers->n_layers; i++) - footnotes = add_footnotes (layers->layers[i].footnotes, - layers->layers[i].n_footnotes, - footnotes, &allocated, &n); - } - - const struct table_item_text *caption = table_item_get_caption (item); - if (caption) - footnotes = add_footnotes (caption->footnotes, caption->n_footnotes, - footnotes, &allocated, &n); - - size_t n_nonnull = 0; - for (size_t i = 0; i < n; i++) - if (footnotes[i]) - footnotes[n_nonnull++] = footnotes[i]; - - *footnotesp = footnotes; - return n_nonnull; -} const char * table_halign_to_string (enum table_halign halign) @@ -284,8 +201,8 @@ font_style_dump (const struct font_style *f) cell_color_dump (&f->fg[0]); putchar ('/'); cell_color_dump (&f->bg[0]); - if (!cell_color_equal (&f->fg[0], &f->fg[1]) - || !cell_color_equal (&f->bg[0], &f->bg[1])) + if (!cell_color_equal (f->fg[0], f->fg[1]) + || !cell_color_equal (f->bg[0], f->bg[1])) { printf (" alt="); cell_color_dump (&f->fg[1]); @@ -300,6 +217,22 @@ font_style_dump (const struct font_style *f) fputs (" underline", stdout); } +bool +font_style_equal (const struct font_style *a, const struct font_style *b) +{ + return (a->bold == b->bold + && a->italic == b->italic + && a->underline == b->underline + && a->markup == b->markup + && cell_color_equal (a->fg[0], b->fg[0]) + && cell_color_equal (a->fg[1], b->fg[1]) + && cell_color_equal (a->bg[0], b->bg[0]) + && cell_color_equal (a->bg[1], b->bg[1]) + && !strcmp (a->typeface ? a->typeface : "", + b->typeface ? b->typeface : "") + && a->size == b->size); +} + void cell_style_dump (const struct cell_style *c) { @@ -312,386 +245,137 @@ cell_style_dump (const struct cell_style *c) c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]); } - -static const bool debugging = true; - /* Creates and returns a new table with NC columns and NR rows and initially no header rows or columns. Sets the number of header rows on each side of TABLE to HL on the - left, HR on the right, HT on the top, HB on the bottom. Header rows + left, HT on the top. Header rows are repeated when a table is broken across multiple columns or multiple pages. The table's cells are initially empty. */ struct table * -table_create (int nc, int nr, int hl, int hr, int ht, int hb) -{ - struct table *t; - - t = pool_create_container (struct table, container); - t->n[TABLE_HORZ] = nc; - t->n[TABLE_VERT] = nr; - t->h[TABLE_HORZ][0] = hl; - t->h[TABLE_HORZ][1] = hr; - t->h[TABLE_VERT][0] = ht; - t->h[TABLE_VERT][1] = hb; - t->ref_cnt = 1; - - t->cc = pool_calloc (t->container, nr * nc, sizeof *t->cc); - t->ct = pool_calloc (t->container, nr * nc, sizeof *t->ct); - - t->rh = pool_nmalloc (t->container, nc, nr + 1); - memset (t->rh, TABLE_STROKE_NONE, nc * (nr + 1)); - - t->rv = pool_nmalloc (t->container, nr, nc + 1); - memset (t->rv, TABLE_STROKE_NONE, nr * (nc + 1)); - - memset (t->styles, 0, sizeof t->styles); - memset (t->rule_colors, 0, sizeof t->rule_colors); - +table_create (int nc, int nr, int hl, int ht) +{ + struct pool *pool = pool_create (); + struct table *t = pool_alloc (pool, sizeof *t); + *t = (struct table) { + .container = pool, + .n = { [H] = nc, [V] = nr }, + .h = { [H] = hl, [V] = ht }, + .ref_cnt = 1, + .cc = pool_calloc (pool, nr * nc, sizeof *t->cc), + .cp = pool_calloc (pool, nr * nc, sizeof *t->cp), + .rh = pool_calloc (pool, nc, nr + 1), + .rv = pool_calloc (pool, nr, nc + 1), + }; return t; } /* Rules. */ /* Draws a vertical line to the left of cells at horizontal position X - from Y1 to Y2 inclusive in style STYLE, if style is not -1. */ + from Y1 to Y2 inclusive in style STYLE. */ void table_vline (struct table *t, int style, int x, int y1, int y2) { - if (debugging) + if (x < 0 || x > t->n[H] || y1 < 0 || y1 > y2 || y2 >= t->n[V]) { - if (x < 0 || x > t->n[H] - || y1 < 0 || y1 >= t->n[V] - || y2 < 0 || y2 >= t->n[V]) - { - printf ("bad vline: x=%d y=(%d,%d) in table size (%d,%d)\n", - x, y1, y2, t->n[H], t->n[V]); - return; - } + printf ("bad vline: x=%d y=(%d,%d) in table size (%d,%d)\n", + x, y1, y2, t->n[H], t->n[V]); + abort (); } - assert (x >= 0); - assert (x <= t->n[H]); - assert (y1 >= 0); - assert (y2 >= y1); - assert (y2 <= t->n[V]); - - if (style != -1) - { - int y; - for (y = y1; y <= y2; y++) - t->rv[x + (t->n[H] + 1) * y] = style; - } + for (int y = y1; y <= y2; y++) + t->rv[x + (t->n[H] + 1) * y] = style; } /* Draws a horizontal line above cells at vertical position Y from X1 - to X2 inclusive in style STYLE, if style is not -1. */ + to X2 inclusive in style STYLE. */ void table_hline (struct table *t, int style, int x1, int x2, int y) { - if (debugging) + if (y < 0 || y > t->n[V] || x1 < 0 || x1 > x2 || x2 >= t->n[H]) { - if (y < 0 || y > t->n[V] - || x1 < 0 || x1 >= t->n[H] - || x2 < 0 || x2 >= t->n[H]) - { - printf ("bad hline: x=(%d,%d) y=%d in table size (%d,%d)\n", - x1, x2, y, t->n[H], t->n[V]); - return; - } + printf ("bad hline: x=(%d,%d) y=%d in table size (%d,%d)\n", + x1, x2, y, t->n[H], t->n[V]); + abort (); } - assert (y >= 0); - assert (y <= t->n[V]); - assert (x2 >= x1); - assert (x1 >= 0); - assert (x2 < t->n[H]); - - if (style != -1) - { - int x; - for (x = x1; x <= x2; x++) - t->rh[x + t->n[H] * y] = style; - } -} - -/* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal - lines of style F_H and vertical lines of style F_V. Fills the - interior of the box with horizontal lines of style I_H and vertical - lines of style I_V. Any of the line styles may be -1 to avoid - drawing those lines. This is distinct from 0, which draws a null - line. */ -void -table_box (struct table *t, int f_h, int f_v, int i_h, int i_v, - int x1, int y1, int x2, int y2) -{ - if (debugging) - { - if (x1 < 0 || x1 >= t->n[H] - || x2 < 0 || x2 >= t->n[H] - || y1 < 0 || y1 >= t->n[V] - || y2 < 0 || y2 >= t->n[V]) - { - printf ("bad box: (%d,%d)-(%d,%d) in table size (%d,%d)\n", - x1, y1, x2, y2, t->n[H], t->n[V]); - NOT_REACHED (); - } - } - - assert (x2 >= x1); - assert (y2 >= y1); - assert (x1 >= 0); - assert (y1 >= 0); - assert (x2 < t->n[H]); - assert (y2 < t->n[V]); - - if (f_h != -1) - { - int x; - for (x = x1; x <= x2; x++) - { - t->rh[x + t->n[H] * y1] = f_h; - t->rh[x + t->n[H] * (y2 + 1)] = f_h; - } - } - if (f_v != -1) - { - int y; - for (y = y1; y <= y2; y++) - { - t->rv[x1 + (t->n[H] + 1) * y] = f_v; - t->rv[(x2 + 1) + (t->n[H] + 1) * y] = f_v; - } - } - - if (i_h != -1) - { - int y; - - for (y = y1 + 1; y <= y2; y++) - { - int x; - - for (x = x1; x <= x2; x++) - t->rh[x + t->n[H] * y] = i_h; - } - } - if (i_v != -1) - { - int x; - - for (x = x1 + 1; x <= x2; x++) - { - int y; - - for (y = y1; y <= y2; y++) - t->rv[x + (t->n[H] + 1) * y] = i_v; - } - } + for (int x = x1; x <= x2; x++) + t->rh[x + t->n[H] * y] = style; } /* Cells. */ -static void -do_table_text (struct table *table, int c, int r, unsigned opt, char *text) -{ - assert (c >= 0); - assert (r >= 0); - assert (c < table->n[H]); - assert (r < table->n[V]); - - if (debugging) - { - if (c < 0 || r < 0 || c >= table->n[H] || r >= table->n[V]) - { - printf ("table_text(): bad cell (%d,%d) in table size (%d,%d)\n", - c, r, table->n[H], table->n[V]); - return; - } - } - - table->cc[c + r * table->n[H]] = text; - table->ct[c + r * table->n[H]] = opt; -} - -/* Sets cell (C,R) in TABLE, with options OPT, to have text value - TEXT. */ -void -table_text (struct table *table, int c, int r, unsigned opt, - const char *text) -{ - do_table_text (table, c, r, opt, pool_strdup (table->container, text)); -} - -/* Sets cell (C,R) in TABLE, with options OPT, to have text value - FORMAT, which is formatted as if passed to printf. */ +/* Fill TABLE cells (X1,X2)-(Y1,Y2), inclusive, with VALUE and OPT. */ void -table_text_format (struct table *table, int c, int r, unsigned opt, - const char *format, ...) -{ - va_list args; - - va_start (args, format); - do_table_text (table, c, r, opt, - pool_vasprintf (table->container, format, args)); - va_end (args); -} - -static struct table_cell * -add_joined_cell (struct table *table, int x1, int y1, int x2, int y2, - unsigned opt) +table_put (struct table *table, int x1, int y1, int x2, int y2, + unsigned int opt, const struct pivot_value *value) { - assert (x1 >= 0); - assert (y1 >= 0); - assert (y2 >= y1); - assert (x2 >= x1); - assert (y2 < table->n[V]); - assert (x2 < table->n[H]); + assert (0 <= x1 && x1 <= x2 && x2 < table->n[H]); + assert (0 <= y1 && y1 <= y2 && y2 < table->n[V]); + const bool debugging = false; if (debugging) { - if (x1 < 0 || x1 >= table->n[H] - || y1 < 0 || y1 >= table->n[V] - || x2 < x1 || x2 >= table->n[H] - || y2 < y1 || y2 >= table->n[V]) - { - printf ("table_joint_text(): bad cell " - "(%d,%d)-(%d,%d) in table size (%d,%d)\n", - x1, y1, x2, y2, table->n[H], table->n[V]); - return NULL; - } + printf ("put "); + if (x1 == x2) + printf ("%d", x1); + else + printf ("%d-%d", x1, x2); + printf (","); + if (y1 == y2) + printf ("%d", y1); + else + printf ("%d-%d", y1, y2); + + char *value_s = value ? pivot_value_to_string (value, NULL) : NULL; + printf (": \"%s\"\n", value_s ? value_s : ""); + free (value_s); } - table_box (table, -1, -1, TABLE_STROKE_NONE, TABLE_STROKE_NONE, - x1, y1, x2, y2); - - struct table_cell *cell = pool_alloc (table->container, sizeof *cell); - *cell = (struct table_cell) { - .d = { [TABLE_HORZ] = { x1, ++x2 }, - [TABLE_VERT] = { y1, ++y2 } }, - .options = opt, - }; - - void **cc = &table->cc[x1 + y1 * table->n[H]]; - unsigned short *ct = &table->ct[x1 + y1 * table->n[H]]; - const int ofs = table->n[H] - (x2 - x1); - for (int y = y1; y < y2; y++) + if (x1 == x2 && y1 == y2) { - for (int x = x1; x < x2; x++) - { - *cc++ = cell; - *ct++ = opt | TAB_JOIN; - } - - cc += ofs; - ct += ofs; + table->cc[x1 + y1 * table->n[H]] = CONST_CAST (struct pivot_value *, value); + table->cp[x1 + y1 * table->n[H]] = opt; } - - return cell; -} - -/* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with - options OPT to have text value TEXT. */ -void -table_joint_text (struct table *table, int x1, int y1, int x2, int y2, - unsigned opt, const char *text) -{ - char *s = pool_strdup (table->container, text); - if (x1 == x2 && y1 == y2) - do_table_text (table, x1, y1, opt, s); - else - add_joined_cell (table, x1, y1, x2, y2, opt)->text = s; -} - -static struct table_cell * -get_joined_cell (struct table *table, int x, int y) -{ - int index = x + y * table->n[H]; - unsigned short opt = table->ct[index]; - struct table_cell *cell; - - if (opt & TAB_JOIN) - cell = table->cc[index]; else { - char *text = table->cc[index]; - - cell = add_joined_cell (table, x, y, x, y, table->ct[index]); - cell->text = text ? text : pool_strdup (table->container, ""); + struct table_cell *cell = pool_alloc (table->container, sizeof *cell); + *cell = (struct table_cell) { + .d = { [H] = { x1, x2 + 1 }, [V] = { y1, y2 + 1 } }, + .options = opt, + .value = value, + }; + + for (int y = y1; y <= y2; y++) + { + size_t ofs = x1 + y * table->n[H]; + void **cc = &table->cc[ofs]; + unsigned char *ct = &table->cp[ofs]; + for (int x = x1; x <= x2; x++) + { + *cc++ = cell; + *ct++ = opt | TABLE_CELL_JOIN; + } + } } - return cell; -} - -/* Sets the subscripts for column X, row Y in TABLE. */ -void -table_add_subscripts (struct table *table, int x, int y, - char **subscripts, size_t n_subscripts) -{ - struct table_cell *cell = get_joined_cell (table, x, y); - - cell->n_subscripts = n_subscripts; - cell->subscripts = pool_nalloc (table->container, n_subscripts, - sizeof *cell->subscripts); - for (size_t i = 0; i < n_subscripts; i++) - cell->subscripts[i] = pool_strdup (table->container, subscripts[i]); } -/* Sets the superscript for column X, row Y in TABLE. */ -void -table_add_superscript (struct table *table, int x, int y, - const char *superscript) -{ - get_joined_cell (table, x, y)->superscript - = pool_strdup (table->container, superscript); -} - -/* Create a footnote in TABLE with MARKER (e.g. "a") as its marker and CONTENT - as its content. The footnote will be styled as STYLE, which is mandatory. - IDX must uniquely identify the footnote within TABLE. - - Returns the new footnote. The return value is the only way to get to the - footnote later, so it is important for the caller to remember it. */ -struct footnote * -table_create_footnote (struct table *table, size_t idx, const char *content, - const char *marker, struct table_area_style *style) +static void +free_value (void *value_) { - assert (style); - - struct footnote *f = pool_alloc (table->container, sizeof *f); - f->idx = idx; - f->content = pool_strdup (table->container, content); - f->marker = pool_strdup (table->container, marker); - f->style = style; - return f; + struct pivot_value *value = value_; + pivot_value_destroy (value); } -/* Attaches a reference to footnote F to the cell at column X, row Y in - TABLE. */ void -table_add_footnote (struct table *table, int x, int y, - const struct footnote *f) +table_put_owned (struct table *table, int x1, int y1, int x2, int y2, + unsigned opt, struct pivot_value *value) { - assert (f->style); - - struct table_cell *cell = get_joined_cell (table, x, y); - - cell->footnotes = pool_realloc ( - table->container, cell->footnotes, - (cell->n_footnotes + 1) * sizeof *cell->footnotes); - - cell->footnotes[cell->n_footnotes++] = f; -} - -/* Overrides the style for column X, row Y in TABLE with STYLE. - Does not make a copy of STYLE, so it should either be allocated from - TABLE->container or have a lifetime that will outlive TABLE. */ -void -table_add_style (struct table *table, int x, int y, - const struct table_area_style *style) -{ - get_joined_cell (table, x, y)->style = style; + table_put (table, x1, y1, x2, y2, opt, value); + pool_register (table->container, free_value, value); } /* Returns true if column C, row R has no contents, otherwise false. */ @@ -702,10 +386,7 @@ table_cell_is_empty (const struct table *table, int c, int r) } /* Initializes CELL with the contents of the table cell at column X and row Y - within TABLE. When CELL is no longer needed, the caller is responsible for - freeing it by calling table_cell_free(CELL). - - The caller must ensure that CELL is destroyed before TABLE is unref'ed. */ + within TABLE. */ void table_get_cell (const struct table *t, int x, int y, struct table_cell *cell) { @@ -713,32 +394,53 @@ table_get_cell (const struct table *t, int x, int y, struct table_cell *cell) assert (y >= 0 && y < t->n[TABLE_VERT]); int index = x + y * t->n[H]; - unsigned short opt = t->ct[index]; + unsigned char opt = t->cp[index]; const void *cc = t->cc[index]; - const struct table_area_style *style - = t->styles[(opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT]; - if (opt & TAB_JOIN) + struct table_area_style *style + = t->styles[(opt & TABLE_CELL_STYLE_MASK) >> TABLE_CELL_STYLE_SHIFT]; + + static const struct pivot_value empty_value = { + .text = { + .type = PIVOT_VALUE_TEXT, + .local = (char *) "", + .c = (char *) "", + .id = (char *) "", + .user_provided = true, + }, + }; + + if (opt & TABLE_CELL_JOIN) { const struct table_cell *jc = cc; *cell = *jc; - if (!cell->style) - cell->style = style; + if (!cell->value) + cell->value = &empty_value; + if (!cell->font_style) + cell->font_style = &style->font_style; + if (!cell->cell_style) + cell->cell_style = &style->cell_style; } else - *cell = (struct table_cell) { - .d = { [TABLE_HORZ] = { x, x + 1 }, - [TABLE_VERT] = { y, y + 1 } }, - .options = opt, - .text = CONST_CAST (char *, cc ? cc : ""), - .style = style, - }; - - assert (cell->style); + { + const struct pivot_value *v = cc ? cc : &empty_value; + const struct pivot_value_ex *ex = pivot_value_ex (v); + *cell = (struct table_cell) { + .d = { [H] = { x, x + 1 }, [V] = { y, y + 1 } }, + .options = opt, + .value = v, + .font_style = ex->font_style ? ex->font_style : &style->font_style, + .cell_style = ex->cell_style ? ex->cell_style : &style->cell_style, + }; + } + + assert (cell->font_style); + assert (cell->cell_style); } -/* Returns one of the TAL_* enumeration constants (declared in output/table.h) - representing a rule running alongside one of the cells in TABLE. +/* Returns one of the TABLE_STROKE_* enumeration constants (declared in + output/table.h) representing a rule running alongside one of the cells in + TABLE. Suppose NC is the number of columns in TABLE and NR is the number of rows. Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR. If (X,Y) = @@ -774,18 +476,17 @@ table_get_cell (const struct table *t, int x, int y, struct table_cell *cell) the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule between that cell and cell (0,1); and so on, up to (0,NR), which runs horizontally below cell (0,NR-1). */ -int -table_get_rule (const struct table *table, enum table_axis axis, int x, int y, - struct cell_color *color) +struct table_border_style +table_get_rule (const struct table *table, enum table_axis axis, int x, int y) { assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ)); assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT)); - uint8_t raw = (axis == TABLE_VERT - ? table->rh[x + table->n[H] * y] - : table->rv[x + (table->n[H] + 1) * y]); - struct cell_color *p = table->rule_colors[(raw & TAB_RULE_STYLE_MASK) - >> TAB_RULE_STYLE_SHIFT]; - *color = p ? *p : (struct cell_color) CELL_COLOR_BLACK; - return (raw & TAB_RULE_TYPE_MASK) >> TAB_RULE_TYPE_SHIFT; + size_t border_idx = (axis == TABLE_VERT + ? table->rh[x + table->n[H] * y] + : table->rv[x + (table->n[H] + 1) * y]); + return (border_idx < table->n_borders + ? table->borders[border_idx] + : (struct table_border_style) { TABLE_STROKE_NONE, + CELL_COLOR_BLACK }); }