From b754fbf65fcd1b4fb466bbba6af71e6717df01e3 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 1 Jan 2019 21:11:20 -0800 Subject: [PATCH] output: Move text_item and group_item usage closer to the SPV model. --- src/language/data-io/print-space.c | 2 +- src/language/lexer/lexer.c | 10 +- src/language/stats/reliability.c | 2 +- src/output/ascii.c | 23 +- src/output/cairo.c | 299 +++++++++++++++++++++++--- src/output/cairo.h | 18 +- src/output/csv.c | 56 +++-- src/output/driver.c | 58 ++--- src/output/html.c | 12 +- src/output/tab.c | 6 +- src/output/text-item.c | 23 ++ src/output/text-item.h | 11 +- src/ui/gui/psppire-output-view.c | 70 +++--- tests/language/control/loop.at | 72 ------- tests/language/data-io/print-space.at | 8 - tests/language/data-io/print.at | 14 -- 16 files changed, 424 insertions(+), 260 deletions(-) diff --git a/src/language/data-io/print-space.c b/src/language/data-io/print-space.c index 9f27da80f4..555cdfa405 100644 --- a/src/language/data-io/print-space.c +++ b/src/language/data-io/print-space.c @@ -134,7 +134,7 @@ print_space_trns_proc (void *t_, struct ccase **c, while (n--) if (trns->writer == NULL) - text_item_submit (text_item_create (TEXT_ITEM_BLANK_LINE, "")); + text_item_submit (text_item_create (TEXT_ITEM_LOG, "")); else dfm_put_record (trns->writer, " ", 1); /* XXX */ diff --git a/src/language/lexer/lexer.c b/src/language/lexer/lexer.c index 2bcb18c328..11de489772 100644 --- a/src/language/lexer/lexer.c +++ b/src/language/lexer/lexer.c @@ -1435,13 +1435,9 @@ lex_source_get__ (const struct lex_source *src_) if (copy_len > 0 && line[copy_len - 1] == '\r') copy_len--; - /* Make a copy of the line with \n end-of-line and null terminator. */ - char *syntax = xmalloc (copy_len + 2); - memcpy (syntax, line, copy_len); - syntax[copy_len] = '\n'; - syntax[copy_len + 1] = '\0'; - - text_item_submit (text_item_create_nocopy (TEXT_ITEM_SYNTAX, syntax)); + /* Submit the line as syntax. */ + text_item_submit (text_item_create_nocopy (TEXT_ITEM_SYNTAX, + xmemdup0 (line, copy_len))); src->journal_pos += line_len; } diff --git a/src/language/stats/reliability.c b/src/language/stats/reliability.c index 756932ba89..e1c205f60f 100644 --- a/src/language/stats/reliability.c +++ b/src/language/stats/reliability.c @@ -522,7 +522,7 @@ do_reliability (struct casereader *input, struct dataset *ds, alpha (s->n_items, s->sum_of_variances, s->variance_of_sums); } - text_item_submit (text_item_create_format (TEXT_ITEM_PARAGRAPH, _("Scale: %s"), + text_item_submit (text_item_create_format (TEXT_ITEM_TITLE, _("Scale: %s"), ds_cstr (&rel->scale_name))); case_processing_summary (n_valid, n_missing, dataset_dict (ds)); diff --git a/src/output/ascii.c b/src/output/ascii.c index 7833ed0ac5..2584faffaa 100644 --- a/src/output/ascii.c +++ b/src/output/ascii.c @@ -184,8 +184,8 @@ struct ascii_driver #ifdef HAVE_CAIRO /* Colours for charts */ - struct xr_color fg; - struct xr_color bg; + struct cell_color fg; + struct cell_color bg; #endif int width; /* Page width. */ @@ -492,7 +492,7 @@ ascii_submit (struct output_driver *driver, struct text_item *text_item; text_item = text_item_create_format ( - TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name); + TEXT_ITEM_LOG, _("See %s for a chart."), file_name); ascii_submit (driver, &text_item->output_item); text_item_unref (text_item); @@ -505,20 +505,9 @@ ascii_submit (struct output_driver *driver, const struct text_item *text_item = to_text_item (output_item); enum text_item_type type = text_item_get_type (text_item); - switch (type) - { - case TEXT_ITEM_PAGE_TITLE: - case TEXT_ITEM_BLANK_LINE: - break; - - case TEXT_ITEM_EJECT_PAGE: - break; - - default: - ascii_output_table_item_unref ( - a, text_item_to_table_item (text_item_ref (text_item))); - break; - } + if (type != TEXT_ITEM_PAGE_TITLE && type != TEXT_ITEM_EJECT_PAGE) + ascii_output_table_item_unref ( + a, text_item_to_table_item (text_item_ref (text_item))); } else if (is_message_item (output_item)) { diff --git a/src/output/cairo.c b/src/output/cairo.c index 80bca382a7..1017354181 100644 --- a/src/output/cairo.c +++ b/src/output/cairo.c @@ -20,6 +20,7 @@ #include "libpspp/assertion.h" #include "libpspp/cast.h" +#include "libpspp/hash-functions.h" #include "libpspp/message.h" #include "libpspp/pool.h" #include "libpspp/start-date.h" @@ -53,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -153,8 +155,8 @@ struct xr_driver int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */ int object_spacing; /* Space between output objects. */ - struct xr_color bg; /* Background color */ - struct xr_color fg; /* Foreground color */ + struct cell_color bg; /* Background color */ + struct cell_color fg; /* Foreground color */ int initial_page_number; @@ -214,6 +216,256 @@ opt (struct output_driver *d, struct string_map *options, const char *key, return driver_option_get (d, options, key, default_value); } +static int +lookup_color_name (const char *s) +{ + struct color + { + struct hmap_node hmap_node; + const char *name; + int code; + }; + + static struct color colors[] = + { + { .name = "aliceblue", .code = 0xf0f8ff }, + { .name = "antiquewhite", .code = 0xfaebd7 }, + { .name = "aqua", .code = 0x00ffff }, + { .name = "aquamarine", .code = 0x7fffd4 }, + { .name = "azure", .code = 0xf0ffff }, + { .name = "beige", .code = 0xf5f5dc }, + { .name = "bisque", .code = 0xffe4c4 }, + { .name = "black", .code = 0x000000 }, + { .name = "blanchedalmond", .code = 0xffebcd }, + { .name = "blue", .code = 0x0000ff }, + { .name = "blueviolet", .code = 0x8a2be2 }, + { .name = "brown", .code = 0xa52a2a }, + { .name = "burlywood", .code = 0xdeb887 }, + { .name = "cadetblue", .code = 0x5f9ea0 }, + { .name = "chartreuse", .code = 0x7fff00 }, + { .name = "chocolate", .code = 0xd2691e }, + { .name = "coral", .code = 0xff7f50 }, + { .name = "cornflowerblue", .code = 0x6495ed }, + { .name = "cornsilk", .code = 0xfff8dc }, + { .name = "crimson", .code = 0xdc143c }, + { .name = "cyan", .code = 0x00ffff }, + { .name = "darkblue", .code = 0x00008b }, + { .name = "darkcyan", .code = 0x008b8b }, + { .name = "darkgoldenrod", .code = 0xb8860b }, + { .name = "darkgray", .code = 0xa9a9a9 }, + { .name = "darkgreen", .code = 0x006400 }, + { .name = "darkgrey", .code = 0xa9a9a9 }, + { .name = "darkkhaki", .code = 0xbdb76b }, + { .name = "darkmagenta", .code = 0x8b008b }, + { .name = "darkolivegreen", .code = 0x556b2f }, + { .name = "darkorange", .code = 0xff8c00 }, + { .name = "darkorchid", .code = 0x9932cc }, + { .name = "darkred", .code = 0x8b0000 }, + { .name = "darksalmon", .code = 0xe9967a }, + { .name = "darkseagreen", .code = 0x8fbc8f }, + { .name = "darkslateblue", .code = 0x483d8b }, + { .name = "darkslategray", .code = 0x2f4f4f }, + { .name = "darkslategrey", .code = 0x2f4f4f }, + { .name = "darkturquoise", .code = 0x00ced1 }, + { .name = "darkviolet", .code = 0x9400d3 }, + { .name = "deeppink", .code = 0xff1493 }, + { .name = "deepskyblue", .code = 0x00bfff }, + { .name = "dimgray", .code = 0x696969 }, + { .name = "dimgrey", .code = 0x696969 }, + { .name = "dodgerblue", .code = 0x1e90ff }, + { .name = "firebrick", .code = 0xb22222 }, + { .name = "floralwhite", .code = 0xfffaf0 }, + { .name = "forestgreen", .code = 0x228b22 }, + { .name = "fuchsia", .code = 0xff00ff }, + { .name = "gainsboro", .code = 0xdcdcdc }, + { .name = "ghostwhite", .code = 0xf8f8ff }, + { .name = "gold", .code = 0xffd700 }, + { .name = "goldenrod", .code = 0xdaa520 }, + { .name = "gray", .code = 0x808080 }, + { .name = "green", .code = 0x008000 }, + { .name = "greenyellow", .code = 0xadff2f }, + { .name = "grey", .code = 0x808080 }, + { .name = "honeydew", .code = 0xf0fff0 }, + { .name = "hotpink", .code = 0xff69b4 }, + { .name = "indianred", .code = 0xcd5c5c }, + { .name = "indigo", .code = 0x4b0082 }, + { .name = "ivory", .code = 0xfffff0 }, + { .name = "khaki", .code = 0xf0e68c }, + { .name = "lavender", .code = 0xe6e6fa }, + { .name = "lavenderblush", .code = 0xfff0f5 }, + { .name = "lawngreen", .code = 0x7cfc00 }, + { .name = "lemonchiffon", .code = 0xfffacd }, + { .name = "lightblue", .code = 0xadd8e6 }, + { .name = "lightcoral", .code = 0xf08080 }, + { .name = "lightcyan", .code = 0xe0ffff }, + { .name = "lightgoldenrodyellow", .code = 0xfafad2 }, + { .name = "lightgray", .code = 0xd3d3d3 }, + { .name = "lightgreen", .code = 0x90ee90 }, + { .name = "lightgrey", .code = 0xd3d3d3 }, + { .name = "lightpink", .code = 0xffb6c1 }, + { .name = "lightsalmon", .code = 0xffa07a }, + { .name = "lightseagreen", .code = 0x20b2aa }, + { .name = "lightskyblue", .code = 0x87cefa }, + { .name = "lightslategray", .code = 0x778899 }, + { .name = "lightslategrey", .code = 0x778899 }, + { .name = "lightsteelblue", .code = 0xb0c4de }, + { .name = "lightyellow", .code = 0xffffe0 }, + { .name = "lime", .code = 0x00ff00 }, + { .name = "limegreen", .code = 0x32cd32 }, + { .name = "linen", .code = 0xfaf0e6 }, + { .name = "magenta", .code = 0xff00ff }, + { .name = "maroon", .code = 0x800000 }, + { .name = "mediumaquamarine", .code = 0x66cdaa }, + { .name = "mediumblue", .code = 0x0000cd }, + { .name = "mediumorchid", .code = 0xba55d3 }, + { .name = "mediumpurple", .code = 0x9370db }, + { .name = "mediumseagreen", .code = 0x3cb371 }, + { .name = "mediumslateblue", .code = 0x7b68ee }, + { .name = "mediumspringgreen", .code = 0x00fa9a }, + { .name = "mediumturquoise", .code = 0x48d1cc }, + { .name = "mediumvioletred", .code = 0xc71585 }, + { .name = "midnightblue", .code = 0x191970 }, + { .name = "mintcream", .code = 0xf5fffa }, + { .name = "mistyrose", .code = 0xffe4e1 }, + { .name = "moccasin", .code = 0xffe4b5 }, + { .name = "navajowhite", .code = 0xffdead }, + { .name = "navy", .code = 0x000080 }, + { .name = "oldlace", .code = 0xfdf5e6 }, + { .name = "olive", .code = 0x808000 }, + { .name = "olivedrab", .code = 0x6b8e23 }, + { .name = "orange", .code = 0xffa500 }, + { .name = "orangered", .code = 0xff4500 }, + { .name = "orchid", .code = 0xda70d6 }, + { .name = "palegoldenrod", .code = 0xeee8aa }, + { .name = "palegreen", .code = 0x98fb98 }, + { .name = "paleturquoise", .code = 0xafeeee }, + { .name = "palevioletred", .code = 0xdb7093 }, + { .name = "papayawhip", .code = 0xffefd5 }, + { .name = "peachpuff", .code = 0xffdab9 }, + { .name = "peru", .code = 0xcd853f }, + { .name = "pink", .code = 0xffc0cb }, + { .name = "plum", .code = 0xdda0dd }, + { .name = "powderblue", .code = 0xb0e0e6 }, + { .name = "purple", .code = 0x800080 }, + { .name = "red", .code = 0xff0000 }, + { .name = "rosybrown", .code = 0xbc8f8f }, + { .name = "royalblue", .code = 0x4169e1 }, + { .name = "saddlebrown", .code = 0x8b4513 }, + { .name = "salmon", .code = 0xfa8072 }, + { .name = "sandybrown", .code = 0xf4a460 }, + { .name = "seagreen", .code = 0x2e8b57 }, + { .name = "seashell", .code = 0xfff5ee }, + { .name = "sienna", .code = 0xa0522d }, + { .name = "silver", .code = 0xc0c0c0 }, + { .name = "skyblue", .code = 0x87ceeb }, + { .name = "slateblue", .code = 0x6a5acd }, + { .name = "slategray", .code = 0x708090 }, + { .name = "slategrey", .code = 0x708090 }, + { .name = "snow", .code = 0xfffafa }, + { .name = "springgreen", .code = 0x00ff7f }, + { .name = "steelblue", .code = 0x4682b4 }, + { .name = "tan", .code = 0xd2b48c }, + { .name = "teal", .code = 0x008080 }, + { .name = "thistle", .code = 0xd8bfd8 }, + { .name = "tomato", .code = 0xff6347 }, + { .name = "turquoise", .code = 0x40e0d0 }, + { .name = "violet", .code = 0xee82ee }, + { .name = "wheat", .code = 0xf5deb3 }, + { .name = "white", .code = 0xffffff }, + { .name = "whitesmoke", .code = 0xf5f5f5 }, + { .name = "yellow", .code = 0xffff00 }, + { .name = "yellowgreen", .code = 0x9acd32 }, + }; + + static struct hmap color_table = HMAP_INITIALIZER (color_table); + + if (hmap_is_empty (&color_table)) + for (size_t i = 0; i < sizeof colors / sizeof *colors; i++) + hmap_insert (&color_table, &colors[i].hmap_node, + hash_string (colors[i].name, 0)); + + const struct color *color; + HMAP_FOR_EACH_WITH_HASH (color, struct color, hmap_node, + hash_string (s, 0), &color_table) + if (!strcmp (color->name, s)) + return color->code; + return -1; +} + +static bool +parse_color__ (const char *s, struct cell_color *color) +{ + /* #rrrrggggbbbb */ + uint16_t r16, g16, b16; + int len; + if (sscanf (s, "#%4"SCNx16"%4"SCNx16"%4"SCNx16"%n", + &r16, &g16, &b16, &len) == 3 + && len == 13 + && !s[len]) + { + color->r = r16 >> 8; + color->g = g16 >> 8; + color->b = b16 >> 8; + return true; + } + + /* #rrggbb */ + uint8_t r, g, b; + if (sscanf (s, "#%2"SCNx8"%2"SCNx8"%2"SCNx8"%n", &r, &g, &b, &len) == 3 + && len == 7 + && !s[len]) + { + color->r = r; + color->g = g; + color->b = b; + return true; + } + + /* rrggbb */ + if (sscanf (s, "%2"SCNx8"%2"SCNx8"%2"SCNx8"%n", &r, &g, &b, &len) == 3 + && len == 6 + && !s[len]) + { + color->r = r; + color->g = g; + color->b = b; + return true; + } + + /* rgb(r,g,b) */ + if (sscanf (s, "rgb (%"SCNi8" , %"SCNi8" , %"SCNi8" ) %n", + &r, &g, &b, &len) == 3 + && !s[len]) + { + color->r = r; + color->g = g; + color->b = b; + return true; + } + + /* rgba(r,g,b,a), ignoring a. */ + if (sscanf (s, "rgba (%"SCNi8" , %"SCNi8" , %"SCNi8", %*f ) %n", + &r, &g, &b, &len) == 3 + && !s[len]) + { + color->r = r; + color->g = g; + color->b = b; + return true; + } + + int code = lookup_color_name (s); + if (code >= 0) + { + color->r = code >> 16; + color->g = code >> 8; + color->b = code; + return true; + } + + return false; +} + /* Parse color information specified by KEY into {RED,GREEN,BLUE}. Currently, the input string must be of the form "#RRRRGGGGBBBB" Future implementations might allow things like "yellow" and @@ -222,27 +474,12 @@ opt (struct output_driver *d, struct string_map *options, const char *key, void parse_color (struct output_driver *d, struct string_map *options, const char *key, const char *default_value, - struct xr_color *color) + struct cell_color *color) { - int red, green, blue; char *string = parse_string (opt (d, options, key, default_value)); - - if (3 != sscanf (string, "#%04x%04x%04x", &red, &green, &blue)) - { - /* If the parsed option string fails, then try the default value */ - if ( 3 != sscanf (default_value, "#%04x%04x%04x", &red, &green, &blue)) - { - /* ... and if that fails set everything to zero */ - red = green = blue = 0; - } - } - + if (!parse_color__ (string, color) && !parse_color__ (default_value, color)) + *color = CELL_COLOR_BLACK; free (string); - - /* Convert 16 bit ints to float */ - color->red = red / (double) 0xFFFF; - color->green = green / (double) 0xFFFF; - color->blue = blue / (double) 0xFFFF; } static PangoFontDescription * @@ -561,7 +798,8 @@ xr_set_cairo (struct xr_driver *xr, cairo_t *cairo) xr->params->rtl = render_direction_rtl (); } - cairo_set_source_rgb (xr->cairo, xr->fg.red, xr->fg.green, xr->fg.blue); + cairo_set_source_rgb (xr->cairo, + xr->fg.r / 255.0, xr->fg.g / 255.0, xr->fg.b / 255.0); } static struct output_driver * @@ -759,7 +997,8 @@ void xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo) { cairo_save (cairo); - cairo_set_source_rgb (cairo, xr->bg.red, xr->bg.green, xr->bg.blue); + cairo_set_source_rgb (cairo, + xr->bg.r / 255.0, xr->bg.g / 255.0, xr->bg.b / 255.0); cairo_rectangle (cairo, 0, 0, xr->width, xr->length); cairo_fill (cairo); cairo_restore (cairo); @@ -1729,9 +1968,8 @@ xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr, char * xr_draw_png_chart (const struct chart_item *item, const char *file_name_template, int number, - const struct xr_color *fg, - const struct xr_color *bg - ) + const struct cell_color *fg, + const struct cell_color *bg) { const int width = 640; const int length = 480; @@ -1752,10 +1990,10 @@ xr_draw_png_chart (const struct chart_item *item, surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length); cr = cairo_create (surface); - cairo_set_source_rgb (cr, bg->red, bg->green, bg->blue); + cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0); cairo_paint (cr); - cairo_set_source_rgb (cr, fg->red, fg->green, fg->blue); + cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0); xr_draw_chart (item, cr, 0.0, 0.0, width, length); @@ -1911,11 +2149,6 @@ xr_render_text (struct xr_driver *xr, const struct text_item *text_item) string_map_replace (&xr->heading_vars, "PageTitle", text); break; - case TEXT_ITEM_BLANK_LINE: - if (xr->y > 0) - xr->y += xr->char_height; - break; - case TEXT_ITEM_EJECT_PAGE: if (xr->y > 0) return xr_render_eject (); @@ -1934,7 +2167,7 @@ xr_render_message (struct xr_driver *xr, const struct message_item *message_item) { char *s = msg_to_string (message_item_get_msg (message_item)); - struct text_item *item = text_item_create (TEXT_ITEM_PARAGRAPH, s); + struct text_item *item = text_item_create (TEXT_ITEM_LOG, s); free (s); return xr_render_table (xr, text_item_to_table_item (item)); } diff --git a/src/output/cairo.h b/src/output/cairo.h index a5fea7f7f2..b1d06922ee 100644 --- a/src/output/cairo.h +++ b/src/output/cairo.h @@ -23,7 +23,9 @@ #include +struct cell_color; struct chart_item; +struct output_driver; struct output_item; struct string_map; @@ -83,26 +85,16 @@ void xr_driver_output_item (struct xr_driver *, const struct output_item *); bool xr_driver_need_new_page (const struct xr_driver *); bool xr_driver_is_page_blank (const struct xr_driver *); -struct xr_color -{ - double red; - double green; - double blue; -}; - -struct output_driver; -struct string_map; - void parse_color (struct output_driver *d, struct string_map *options, const char *key, const char *default_value, - struct xr_color *color); + struct cell_color *); /* Render charts with Cairo. */ char *xr_draw_png_chart (const struct chart_item *, const char *file_name_template, int number, - const struct xr_color *fg, - const struct xr_color *bg); + const struct cell_color *fg, + const struct cell_color *bg); #endif /* HAVE_CAIRO */ diff --git a/src/output/csv.c b/src/output/csv.c index 0067c61202..65566c4e3a 100644 --- a/src/output/csv.c +++ b/src/output/csv.c @@ -128,26 +128,49 @@ csv_flush (struct output_driver *driver) } static void -csv_output_field (struct csv_driver *csv, const char *field) +csv_output_field__ (struct csv_driver *csv, struct substring field) { - while (*field == ' ') - field++; + ss_ltrim (&field, ss_cstr (" ")); - if (csv->quote && field[strcspn (field, csv->quote_set)]) + if (csv->quote && ss_cspan (field, ss_cstr (csv->quote_set)) < field.length) { - const char *p; - putc (csv->quote, csv->file); - for (p = field; *p != '\0'; p++) + for (size_t i = 0; i < field.length; i++) { - if (*p == csv->quote) + if (field.string[i] == csv->quote) putc (csv->quote, csv->file); - putc (*p, csv->file); + putc (field.string[i], csv->file); } putc (csv->quote, csv->file); } else - fputs (field, csv->file); + fwrite (field.string, field.length, 1, csv->file); +} + +static void +csv_output_field (struct csv_driver *csv, const char *field) +{ + csv_output_field__ (csv, ss_cstr (field)); +} + +static void +csv_put_separator (struct csv_driver *csv) +{ + if (csv->n_items++ > 0) + putc ('\n', csv->file); +} + +static void +csv_output_lines (struct csv_driver *csv, const char *text_) +{ + struct substring text = ss_cstr (text_); + struct substring line; + size_t save_idx = 0; + while (ss_separate (text, ss_cstr ("\n"), &save_idx, &line)) + { + csv_output_field__ (csv, line); + putc ('\n', csv->file); + } } static void @@ -173,13 +196,6 @@ csv_output_table_item_text (struct csv_driver *csv, putc ('\n', csv->file); } -static void -csv_put_separator (struct csv_driver *csv) -{ - if (csv->n_items++ > 0) - putc ('\n', csv->file); -} - static void csv_submit (struct output_driver *driver, const struct output_item *output_item) @@ -268,15 +284,15 @@ csv_submit (struct output_driver *driver, return; csv_put_separator (csv); + if (text_item->markup) { char *plain_text = output_get_text_from_markup (text); - csv_output_field (csv, plain_text); + csv_output_lines (csv, plain_text); free (plain_text); } else - csv_output_field (csv, text); - putc ('\n', csv->file); + csv_output_lines (csv, text); } else if (is_message_item (output_item)) { diff --git a/src/output/driver.c b/src/output/driver.c index fb5444ec14..d37ec8b7d7 100644 --- a/src/output/driver.c +++ b/src/output/driver.c @@ -52,7 +52,8 @@ struct output_engine { struct llx_list drivers; /* Contains "struct output_driver"s. */ - struct string deferred_syntax; /* TEXT_ITEM_SYNTAX being accumulated. */ + struct string deferred_text; /* Output text being accumulated. */ + enum text_item_type deferred_type; /* Type of text being accumulated. */ char *command_name; /* Name of command being processed. */ char *title, *subtitle; /* Components of page title. */ @@ -104,7 +105,7 @@ output_engine_push (void) e = &engine_stack[n_stack++]; memset (e, 0, sizeof *e); llx_init (&e->drivers); - ds_init_empty (&e->deferred_syntax); + ds_init_empty (&e->deferred_text); string_map_init (&e->heading_vars); @@ -126,7 +127,7 @@ output_engine_pop (void) struct output_driver *d = llx_pop_head (&e->drivers, &llx_malloc_mgr); output_driver_destroy (d); } - ds_destroy (&e->deferred_syntax); + ds_destroy (&e->deferred_text); free (e->command_name); free (e->title); free (e->subtitle); @@ -179,25 +180,39 @@ output_submit__ (struct output_engine *e, struct output_item *item) } static void -flush_deferred_syntax (struct output_engine *e) +flush_deferred_text (struct output_engine *e) { - if (!ds_is_empty (&e->deferred_syntax)) + if (!ds_is_empty (&e->deferred_text)) { - ds_trim (&e->deferred_syntax, ss_cstr ("\n")); - if (!ds_is_empty (&e->deferred_syntax)) - { - char *syntax = ds_steal_cstr (&e->deferred_syntax); - output_submit__ (e, text_item_super (text_item_create_nocopy ( - TEXT_ITEM_SYNTAX, syntax))); - } + char *text = ds_steal_cstr (&e->deferred_text); + output_submit__ (e, text_item_super (text_item_create_nocopy ( + e->deferred_type, text))); } } static bool -is_syntax_item (const struct output_item *item) +defer_text (struct output_engine *e, struct output_item *item) { - return (is_text_item (item) - && text_item_get_type (to_text_item (item)) == TEXT_ITEM_SYNTAX); + if (!is_text_item (item)) + return false; + + enum text_item_type type = text_item_get_type (to_text_item (item)); + if (type != TEXT_ITEM_SYNTAX && type != TEXT_ITEM_LOG) + return false; + + if (!ds_is_empty (&e->deferred_text) && e->deferred_type != type) + flush_deferred_text (e); + + e->deferred_type = type; + + if (!ds_is_empty (&e->deferred_text)) + ds_put_byte (&e->deferred_text, '\n'); + + const char *text = text_item_get_text (to_text_item (item)); + ds_put_cstr (&e->deferred_text, text); + output_item_unref (item); + + return true; } /* Submits ITEM to the configured output drivers, and transfers ownership to @@ -210,14 +225,9 @@ output_submit (struct output_item *item) if (item == NULL) return; - if (is_syntax_item (item)) - { - ds_put_cstr (&e->deferred_syntax, text_item_get_text (to_text_item (item))); - output_item_unref (item); - return; - } - - flush_deferred_syntax (e); + if (defer_text (e, item)) + return; + flush_deferred_text (e); if (is_group_open_item (item)) { @@ -285,7 +295,7 @@ output_flush (void) struct output_engine *e = engine_stack_top (); struct llx *llx; - flush_deferred_syntax (e); + flush_deferred_text (e); for (llx = llx_head (&e->drivers); llx != llx_null (&e->drivers); llx = llx_next (llx)) { diff --git a/src/output/html.c b/src/output/html.c index f41a984cf7..1027dea501 100644 --- a/src/output/html.c +++ b/src/output/html.c @@ -50,8 +50,8 @@ struct html_driver { struct output_driver driver; #ifdef HAVE_CAIRO - struct xr_color fg; - struct xr_color bg; + struct cell_color fg; + struct cell_color bg; #endif struct file_handle *handle; char *chart_file_name; @@ -279,18 +279,10 @@ html_submit (struct output_driver *driver, fprintf (html->file, "\n"); break; - case TEXT_ITEM_PARAGRAPH: - print_title_tag (html->file, "P", s); - break; - case TEXT_ITEM_LOG: print_title_tag (html->file, "PRE", s); /* should be

*/ break; - case TEXT_ITEM_BLANK_LINE: - fputs ("
", html->file); - break; - case TEXT_ITEM_EJECT_PAGE: /* Nothing to do. */ break; diff --git a/src/output/tab.c b/src/output/tab.c index 19f7072dd0..df0a052234 100644 --- a/src/output/tab.c +++ b/src/output/tab.c @@ -440,11 +440,9 @@ tab_cell_is_empty (const struct tab_table *table, int c, int r) This function is obsolete. Please do not add new uses of it. Instead, use a text_item (see output/text-item.h). */ void -tab_output_text (int options, const char *string) +tab_output_text (int options UNUSED, const char *string) { - enum text_item_type type = (options & TAB_FIX ? TEXT_ITEM_LOG - : TEXT_ITEM_PARAGRAPH); - text_item_submit (text_item_create (type, string)); + text_item_submit (text_item_create (TEXT_ITEM_LOG, string)); } /* Same as tab_output_text(), but FORMAT is passed through printf-like diff --git a/src/output/text-item.c b/src/output/text-item.c index eb3155affc..7cd1ef3841 100644 --- a/src/output/text-item.c +++ b/src/output/text-item.c @@ -36,6 +36,29 @@ #include "gettext.h" #define _(msgid) gettext (msgid) +const char * +text_item_type_to_string (enum text_item_type type) +{ + switch (type) + { + case TEXT_ITEM_PAGE_TITLE: + return _("Page Title"); + + case TEXT_ITEM_TITLE: + return _("Title"); + + case TEXT_ITEM_SYNTAX: + case TEXT_ITEM_LOG: + return _("Log"); + + case TEXT_ITEM_EJECT_PAGE: + return _("Page Break"); + + default: + return _("Text"); + } +} + /* Creates and returns a new text item containing TEXT and the specified TYPE. The new text item takes ownership of TEXT. */ struct text_item * diff --git a/src/output/text-item.h b/src/output/text-item.h index 2e27529fec..9efc26490b 100644 --- a/src/output/text-item.h +++ b/src/output/text-item.h @@ -32,18 +32,13 @@ enum text_item_type { TEXT_ITEM_PAGE_TITLE, /* TITLE and SUBTITLE commands. */ TEXT_ITEM_TITLE, /* Title. */ - TEXT_ITEM_PARAGRAPH, /* Normal paragraph of text. */ - - /* Log items. */ - TEXT_ITEM_SYNTAX, /* A single line of PSPP syntax. */ + TEXT_ITEM_SYNTAX, /* Syntax printback logging. */ TEXT_ITEM_LOG, /* Other logging. */ - - /* Spacing. Some output drivers that are not based on lines and pages - (e.g. CSV, HTML) may ignore these. */ - TEXT_ITEM_BLANK_LINE, /* Blank line. */ TEXT_ITEM_EJECT_PAGE /* Eject page. */ }; +const char *text_item_type_to_string (enum text_item_type); + /* A text item. */ struct text_item { diff --git a/src/ui/gui/psppire-output-view.c b/src/ui/gui/psppire-output-view.c index 3a7ce4d357..2f92b2696a 100644 --- a/src/ui/gui/psppire-output-view.c +++ b/src/ui/gui/psppire-output-view.c @@ -30,6 +30,7 @@ #include "output/group-item.h" #include "output/message-item.h" #include "output/output-item.h" +#include "output/output-item-provider.h" #include "output/table-item.h" #include "output/text-item.h" @@ -59,8 +60,7 @@ struct psppire_output_view struct string_map render_opts; GtkTreeView *overview; - GtkTreeIter cur_command; - bool in_command; + GtkTreePath *cur_group; GtkWidget *toplevel; @@ -245,6 +245,9 @@ rerender (struct psppire_output_view *view) if (view->y > 0) view->y += view->font_height / 2; + if (is_group_open_item (item->item)) + continue; + r = xr_rendering_create (view->xr, item->item, cr); if (r == NULL) { @@ -304,21 +307,21 @@ psppire_output_view_put (struct psppire_output_view *view, { struct output_view_item *view_item; GtkWidget *drawing_area; - struct xr_rendering *r; struct string name; - GtkTreeStore *store; cairo_t *cr = NULL; - GtkTreePath *path; - GtkTreeIter iter; int tw, th; if (is_group_close_item (item)) { - if (output_get_group_level () == 0) + if (view->cur_group) { - view->in_command = false; - return; + if (!gtk_tree_path_up (view->cur_group)) + { + gtk_tree_path_free (view->cur_group); + view->cur_group = NULL; + } } + return; } else if (is_text_item (item)) { @@ -335,7 +338,9 @@ psppire_output_view_put (struct psppire_output_view *view, view_item->item = output_item_ref (item); view_item->drawing_area = NULL; - if (gtk_widget_get_window (GTK_WIDGET (view->output))) + if (is_group_open_item (item)) + tw = th = 0; + else if (gtk_widget_get_window (GTK_WIDGET (view->output))) { view_item->drawing_area = drawing_area = gtk_drawing_area_new (); @@ -346,7 +351,7 @@ psppire_output_view_put (struct psppire_output_view *view, if (view->y > 0) view->y += view->font_height / 2; - r = xr_rendering_create (view->xr, item, cr); + struct xr_rendering *r = xr_rendering_create (view->xr, item, cr); if (r == NULL) goto done; @@ -357,29 +362,38 @@ psppire_output_view_put (struct psppire_output_view *view, else tw = th = 0; - if (view->overview - && (!is_text_item (item) - || text_item_get_type (to_text_item (item)) != TEXT_ITEM_SYNTAX - || !view->in_command)) + if (view->overview) { - store = GTK_TREE_STORE (gtk_tree_view_get_model (view->overview)); + GtkTreeStore *store = GTK_TREE_STORE ( + gtk_tree_view_get_model (view->overview)); ds_init_empty (&name); - if (is_group_open_item (item) && output_get_group_level () == 1) - { - gtk_tree_store_append (store, &iter, NULL); - view->cur_command = iter; /* XXX shouldn't save a GtkTreeIter */ - view->in_command = true; - } + + /* Create a new node in the tree and puts a reference to it in 'iter'. */ + GtkTreeIter iter; + GtkTreeIter parent; + if (view->cur_group + && gtk_tree_path_get_depth (view->cur_group) > 0 + && gtk_tree_model_get_iter (GTK_TREE_MODEL (store), + &parent, view->cur_group)) + gtk_tree_store_append (store, &iter, &parent); else + gtk_tree_store_append (store, &iter, NULL); + + if (is_group_open_item (item)) { - GtkTreeIter *p = view->in_command ? &view->cur_command : NULL; - gtk_tree_store_append (store, &iter, p); + gtk_tree_path_free (view->cur_group); + view->cur_group = gtk_tree_model_get_path (GTK_TREE_MODEL (store), + &iter); } ds_clear (&name); if (is_text_item (item)) - ds_put_cstr (&name, text_item_get_text (to_text_item (item))); + { + const struct text_item *text_item = to_text_item (item); + ds_put_cstr (&name, text_item_type_to_string ( + text_item_get_type (text_item))); + } else if (is_message_item (item)) { const struct message_item *msg_item = to_message_item (item); @@ -413,7 +427,8 @@ psppire_output_view_put (struct psppire_output_view *view, -1); ds_destroy (&name); - path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter); + GtkTreePath *path = gtk_tree_model_get_path ( + GTK_TREE_MODEL (store), &iter); gtk_tree_view_expand_row (view->overview, path, TRUE); gtk_tree_path_free (path); } @@ -704,8 +719,7 @@ psppire_output_view_new (GtkLayout *output, GtkTreeView *overview) view->y = 0; string_map_init (&view->render_opts); view->overview = overview; - memset (&view->cur_command, 0, sizeof view->cur_command); - view->in_command = false; + view->cur_group = NULL; view->toplevel = gtk_widget_get_toplevel (GTK_WIDGET (output)); view->items = NULL; view->n_items = view->allocated_items = 0; diff --git a/tests/language/control/loop.at b/tests/language/control/loop.at index 60baac7e9c..d506a4d37f 100644 --- a/tests/language/control/loop.at +++ b/tests/language/control/loop.at @@ -38,25 +38,15 @@ execute. AT_CHECK([pspp -o pspp.csv loop.sps]) AT_CHECK([cat pspp.csv], [0], [dnl 1.00 @&t@ - 2.00 @&t@ - -------- - 2.00 @&t@ - 4.00 @&t@ - -------- - 3.00 @&t@ - 6.00 @&t@ - 9.00 @&t@ - -------- - -------- ]) AT_CLEANUP @@ -75,25 +65,15 @@ execute. AT_CHECK([pspp -o pspp.csv loop.sps]) AT_CHECK([cat pspp.csv], [0], [dnl 1.00 @&t@ - 2.00 @&t@ - -------- - 2.00 @&t@ - 4.00 @&t@ - -------- - 3.00 @&t@ - 6.00 @&t@ - 9.00 @&t@ - -------- - -------- ]) AT_CLEANUP @@ -112,27 +92,16 @@ execute. AT_CHECK([pspp -o pspp.csv loop.sps]) AT_CHECK([cat pspp.csv], [0], [dnl 1.00 @&t@ - 2.00 @&t@ - -------- - 2.00 @&t@ - 4.00 @&t@ - -------- - 3.00 @&t@ - 6.00 @&t@ - 9.00 @&t@ - -------- - 4.00 @&t@ - -------- ]) AT_CLEANUP @@ -149,19 +118,12 @@ execute. AT_CHECK([pspp -o pspp.csv loop.sps]) AT_CHECK([cat pspp.csv], [0], [dnl 1.00 @&t@ - 2.00 @&t@ - -------- - 2.00 @&t@ - -------- - 3.00 @&t@ - -------- - -------- ]) AT_CLEANUP @@ -178,23 +140,14 @@ execute. AT_CHECK([pspp -o pspp.csv loop.sps]) AT_CHECK([cat pspp.csv], [0], [dnl 1.00 @&t@ - 2.00 @&t@ - -------- - 2.00 @&t@ - 4.00 @&t@ - -------- - 3.00 @&t@ - 6.00 @&t@ - -------- - -------- ]) AT_CLEANUP @@ -211,15 +164,10 @@ execute. AT_CHECK([pspp -o pspp.csv loop.sps]) AT_CHECK([cat pspp.csv], [0], [dnl -------- - 2.00 @&t@ - 4.00 @&t@ - -------- - -------- - -------- ]) AT_CLEANUP @@ -241,23 +189,14 @@ execute. AT_CHECK([pspp -o pspp.csv loop.sps]) AT_CHECK([cat pspp.csv], [0], [dnl 1.00 @&t@ - -------- - 2.00 @&t@ - 4.00 @&t@ - -------- - 3.00 @&t@ - 6.00 @&t@ - -------- - 4.00 @&t@ - -------- ]) AT_CLEANUP @@ -276,27 +215,16 @@ execute. AT_CHECK([pspp -o pspp.csv loop.sps]) AT_CHECK([cat pspp.csv], [0], [dnl 1 1.00 @&t@ - 1 2.00 @&t@ - -------- - 2 3.00 @&t@ - 2 4.00 @&t@ - -------- - 3 5.00 @&t@ - 3 6.00 @&t@ - -------- - 4 7.00 @&t@ - 4 8.00 @&t@ - -------- ]) AT_CLEANUP diff --git a/tests/language/data-io/print-space.at b/tests/language/data-io/print-space.at index f3367f1b2e..eb3cbe75a8 100644 --- a/tests/language/data-io/print-space.at +++ b/tests/language/data-io/print-space.at @@ -30,11 +30,8 @@ EXECUTE. AT_CHECK([pspp -O format=csv print-space.sps], [0], [dnl 1 @&t@ - - 2 @&t@ - ]) AT_CLEANUP @@ -53,14 +50,9 @@ AT_CHECK([pspp -O format=csv print-space.sps], [0], [dnl 1 @&t@ - - - 2 @&t@ - - ]) AT_CLEANUP diff --git a/tests/language/data-io/print.at b/tests/language/data-io/print.at index 02c18c619f..0e32361b71 100644 --- a/tests/language/data-io/print.at +++ b/tests/language/data-io/print.at @@ -57,50 +57,36 @@ AT_CHECK([pspp -O format=csv print.sps], [0], [dnl 12 - 1 -2 @&t@ - - 3 4 @&t@ 34 - 3 -4 @&t@ - - . 6 @&t@ .6 - . -6 @&t@ - - 7 . @&t@ 7. - 7 -. @&t@ - - 9 0 @&t@ 90 - 9 -0 @&t@ - ]) AT_CLEANUP -- 2.30.2