X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Ftable.c;h=3d07eb40d47563b27c059ee36d0473ce94d85c47;hb=dfb794fa53a423c1f20c3a21811c0bec4a64a916;hp=8f717ead5679359b4f5c8529811e7b731d9c307d;hpb=15cf0336e0bf182212160ca11895dea92f143884;p=pspp diff --git a/src/output/table.c b/src/output/table.c index 8f717ead56..3d07eb40d4 100644 --- a/src/output/table.c +++ b/src/output/table.c @@ -29,11 +29,16 @@ #include "libpspp/compiler.h" #include "libpspp/pool.h" #include "libpspp/str.h" -#include "output/table-item.h" -#include "output/tab.h" +#include "output/output-item.h" +#include "output/pivot-table.h" +#include "output/table.h" #include "gl/xalloc.h" +/* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */ +#define H TABLE_HORZ +#define V TABLE_VERT + /* Increases TABLE's reference count, indicating that it has an additional owner. An table that is shared among multiple owners must not be modified. */ @@ -54,7 +59,7 @@ table_unref (struct table *table) { assert (table->ref_cnt > 0); if (--table->ref_cnt == 0) - table->klass->destroy (table); + pool_destroy (table->container); } } @@ -65,79 +70,11 @@ table_is_shared (const struct table *table) { return table->ref_cnt > 1; } - -/* Sets the number of left header columns in TABLE to HL. */ -void -table_set_hl (struct table *table, int hl) -{ - assert (!table_is_shared (table)); - table->h[TABLE_HORZ][0] = hl; -} - -/* Sets the number of right header columns in TABLE to HR. */ -void -table_set_hr (struct table *table, int hr) -{ - assert (!table_is_shared (table)); - table->h[TABLE_HORZ][1] = hr; -} - -/* Sets the number of top header rows in TABLE to HT. */ -void -table_set_ht (struct table *table, int ht) -{ - assert (!table_is_shared (table)); - table->h[TABLE_VERT][0] = ht; -} - -/* Sets the number of top header rows in TABLE to HB. */ -void -table_set_hb (struct table *table, int hb) -{ - assert (!table_is_shared (table)); - table->h[TABLE_VERT][1] = hb; -} -/* Initializes TABLE as a table of the specified CLASS, initially with a - reference count of 1. - - TABLE initially has 0 rows and columns and no headers. The table - implementation should update the numbers of rows and columns. The table - implementation (or its client) may update the header rows and columns. - - A table is an abstract class, that is, a plain struct table is not useful on - its own. Thus, this function is normally called from the initialization - function of some subclass of table. */ -void -table_init (struct table *table, const struct table_class *class) +struct table_area_style * +table_area_style_clone (struct pool *pool, const struct table_area_style *old) { - table->klass = class; - table->n[TABLE_HORZ] = table->n[TABLE_VERT] = 0; - table->h[TABLE_HORZ][0] = table->h[TABLE_HORZ][1] = 0; - table->h[TABLE_VERT][0] = table->h[TABLE_VERT][1] = 0; - table->ref_cnt = 1; -} - -/* Sets the number of columns in TABLE to NC. */ -void -table_set_nc (struct table *table, int nc) -{ - assert (!table_is_shared (table)); - table->n[TABLE_HORZ] = nc; -} - -/* Sets the number of rows in TABLE to NR. */ -void -table_set_nr (struct table *table, int nr) -{ - assert (!table_is_shared (table)); - table->n[TABLE_VERT] = nr; -} - -struct area_style * -area_style_clone (struct pool *pool, const struct area_style *old) -{ - struct area_style *new = pool_malloc (pool, sizeof *new); + struct table_area_style *new = pool_malloc (pool, sizeof *new); *new = *old; if (new->font_style.typeface) new->font_style.typeface = pool_strdup (pool, new->font_style.typeface); @@ -145,7 +82,7 @@ area_style_clone (struct pool *pool, const struct area_style *old) } void -area_style_free (struct area_style *style) +table_area_style_free (struct table_area_style *style) { if (style) { @@ -153,160 +90,6 @@ area_style_free (struct area_style *style) free (style); } } - -/* 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. */ -void -table_get_cell (const struct table *table, int x, int y, - struct table_cell *cell) -{ - assert (x >= 0 && x < table->n[TABLE_HORZ]); - assert (y >= 0 && y < table->n[TABLE_VERT]); - - static const struct area_style default_style = AREA_STYLE_INITIALIZER; - cell->style = &default_style; - - table->klass->get_cell (table, x, y, cell); -} - -/* Frees CELL, which should have been initialized by calling - table_get_cell(). */ -void -table_cell_free (struct table_cell *cell) -{ - if (cell->destructor != NULL) - cell->destructor (cell->destructor_aux); -} - -/* Returns one of the TAL_* 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) = - (0,0), the return value is the rule that runs vertically on the left side of - cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and - cell (1,0); and so on, up to (NC,0), which runs vertically on the right of - cell (NC-1,0). - - The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ - within a 7x7 table. The '|' characters at the intersection of the X labels - and Y labels show the rule whose style would be returned by calling - table_get_rule with those X and Y values: - - 0 1 2 3 4 5 6 7 - +--+--+--+--+--+--+--+ - 0 | | | | | | | | - +--+--+--+--+--+--+--+ - 1 | | | | | | | | - +--+--+--+--+--+--+--+ - 2 | | | | | | | | - +--+--+--+--+--+--+--+ - 3 | | | | | | | | - +--+--+--+--+--+--+--+ - 4 | | | | | | | | - +--+--+--+--+--+--+--+ - 5 | | | | | | | | - +--+--+--+--+--+--+--+ - 6 | | | | | | | | - +--+--+--+--+--+--+--+ - - Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR. If - (X,Y) = (0,0), the return value is the rule that runs horizontally above - 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) -{ - assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ)); - assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT)); - *color = (struct cell_color) CELL_COLOR_BLACK; - return table->klass->get_rule (table, axis, x, y, color); -} - -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 < table_nr (t); y++) - { - struct table_cell cell; - for (int x = 0; x < table_nc (t); 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); - table_cell_free (&cell); - } - } - - 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_text *caption = table_item_get_caption (item); - if (caption) - footnotes = add_footnotes (caption->footnotes, caption->n_footnotes, - footnotes, &allocated, &n); - - *footnotesp = footnotes; - return n; -} - -/* Returns a table that contains a single cell, whose contents are the - left-aligned TEXT. */ -struct table * -table_from_string (const char *text) -{ - struct tab_table *t = tab_create (1, 1); - tab_text (t, 0, 0, TAB_LEFT, text); - return &t->table; -} const char * table_halign_to_string (enum table_halign halign) @@ -356,11 +139,12 @@ table_halign_interpret (enum table_halign halign, bool numeric) } void -font_style_copy (struct font_style *dst, const struct font_style *src) +font_style_copy (struct pool *container, + struct font_style *dst, const struct font_style *src) { *dst = *src; if (dst->typeface) - dst->typeface = xstrdup (dst->typeface); + dst->typeface = pool_strdup (container, dst->typeface); } void @@ -371,14 +155,15 @@ font_style_uninit (struct font_style *font) } void -area_style_copy (struct area_style *dst, const struct area_style *src) +table_area_style_copy (struct pool *container, struct table_area_style *dst, + const struct table_area_style *src) { - font_style_copy (&dst->font_style, &src->font_style); + font_style_copy (container, &dst->font_style, &src->font_style); dst->cell_style = src->cell_style; } void -area_style_uninit (struct area_style *area) +table_area_style_uninit (struct table_area_style *area) { if (area) font_style_uninit (&area->font_style); @@ -416,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]); @@ -432,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) { @@ -443,3 +244,249 @@ cell_style_dump (const struct cell_style *c) c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1], c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]); } + +/* 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, 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 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. */ +void +table_vline (struct table *t, int style, int x, int y1, int y2) +{ + if (x < 0 || x > t->n[H] || y1 < 0 || y1 > y2 || 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]); + abort (); + } + + 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. */ +void +table_hline (struct table *t, int style, int x1, int x2, int y) +{ + if (y < 0 || y > t->n[V] || x1 < 0 || x1 > x2 || 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]); + abort (); + } + + for (int x = x1; x <= x2; x++) + t->rh[x + t->n[H] * y] = style; +} + +/* Cells. */ + +/* Fill TABLE cells (X1,X2)-(Y1,Y2), inclusive, with VALUE and OPT. */ +void +table_put (struct table *table, int x1, int y1, int x2, int y2, + unsigned int opt, const struct pivot_value *value) +{ + assert (0 <= x1 && x1 <= x2 && x2 < table->n[H]); + assert (0 <= y1 && y1 <= y2 && y2 < table->n[V]); + + const bool debugging = false; + if (debugging) + { + 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); + } + + if (x1 == x2 && y1 == y2) + { + table->cc[x1 + y1 * table->n[H]] = CONST_CAST (struct pivot_value *, value); + table->cp[x1 + y1 * table->n[H]] = opt; + } + else + { + 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; + } + } + } +} + +static void +free_value (void *value_) +{ + struct pivot_value *value = value_; + pivot_value_destroy (value); +} + +void +table_put_owned (struct table *table, int x1, int y1, int x2, int y2, + unsigned opt, struct pivot_value *value) +{ + 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. */ +bool +table_cell_is_empty (const struct table *table, int c, int r) +{ + return table->cc[c + r * table->n[H]] == NULL; +} + +/* Initializes CELL with the contents of the table cell at column X and row Y + within TABLE. */ +void +table_get_cell (const struct table *t, int x, int y, struct table_cell *cell) +{ + assert (x >= 0 && x < t->n[TABLE_HORZ]); + assert (y >= 0 && y < t->n[TABLE_VERT]); + + int index = x + y * t->n[H]; + unsigned char opt = t->cp[index]; + const void *cc = t->cc[index]; + + 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->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 + { + 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 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) = + (0,0), the return value is the rule that runs vertically on the left side of + cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and + cell (1,0); and so on, up to (NC,0), which runs vertically on the right of + cell (NC-1,0). + + The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ + within a 7x7 table. The '|' characters at the intersection of the X labels + and Y labels show the rule whose style would be returned by calling + table_get_rule with those X and Y values: + + 0 1 2 3 4 5 6 7 + +--+--+--+--+--+--+--+ + 0 | | | | | | | | + +--+--+--+--+--+--+--+ + 1 | | | | | | | | + +--+--+--+--+--+--+--+ + 2 | | | | | | | | + +--+--+--+--+--+--+--+ + 3 | | | | | | | | + +--+--+--+--+--+--+--+ + 4 | | | | | | | | + +--+--+--+--+--+--+--+ + 5 | | | | | | | | + +--+--+--+--+--+--+--+ + 6 | | | | | | | | + +--+--+--+--+--+--+--+ + + Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR. If + (X,Y) = (0,0), the return value is the rule that runs horizontally above + 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). */ +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)); + + 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 }); +}