output: Move text_item and group_item usage closer to the SPV model.
authorBen Pfaff <blp@cs.stanford.edu>
Wed, 2 Jan 2019 05:11:20 +0000 (21:11 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 10 Feb 2019 00:00:47 +0000 (16:00 -0800)
16 files changed:
src/language/data-io/print-space.c
src/language/lexer/lexer.c
src/language/stats/reliability.c
src/output/ascii.c
src/output/cairo.c
src/output/cairo.h
src/output/csv.c
src/output/driver.c
src/output/html.c
src/output/tab.c
src/output/text-item.c
src/output/text-item.h
src/ui/gui/psppire-output-view.c
tests/language/control/loop.at
tests/language/data-io/print-space.at
tests/language/data-io/print.at

index 9f27da80f495b396bfebebab7c210de952a593cc..555cdfa405031dde23a81f4e6f3a87a5643a5649 100644 (file)
@@ -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 */
 
index 2bcb18c32854748d206d1c87909bddb27b5b8a9e..11de4897724f1e4f8631e41c715e88f54c61ebd5 100644 (file)
@@ -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;
     }
index 756932ba89545c7188246af3c3f3343cbde9c416..e1c205f60f608c8667b51c140d1c2871dc405701 100644 (file)
@@ -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));
index 7833ed0ac5e34907f90ff8b0d6a630ccc61905b7..2584faffaacf22777388bb4a9170a5c9b3cf037e 100644 (file)
@@ -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))
     {
index 80bca382a7f3159a9b8561e9ab54695c416ed431..101735418142f9e63b41bb29fbe2caa451461982 100644 (file)
@@ -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 <cairo/cairo-ps.h>
 #include <cairo/cairo-svg.h>
 #include <cairo/cairo.h>
+#include <inttypes.h>
 #include <math.h>
 #include <pango/pango-font.h>
 #include <pango/pango-layout.h>
@@ -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));
 }
index a5fea7f7f244d7aedf00a903f5ed0c3adf3a6210..b1d06922eef7903e0c7972e911e6c6c58b2ad286 100644 (file)
@@ -23,7 +23,9 @@
 
 #include <cairo/cairo.h>
 
+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 */
index 0067c61202e306c41fd36e0d991ae94f919536cd..65566c4e3a624a2ba5b02589438b019e52624e0c 100644 (file)
@@ -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))
     {
index fb5444ec14cb2dc76fa64fbef7dfd97f31c75e76..d37ec8b7d79229eff8e224120ab8c49a4e7f414f 100644 (file)
@@ -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))
     {
index f41a984cf7898f32dee9482de3393eb282546d56..1027dea501037e5e74655ecb0a4d76b7c2357280 100644 (file)
@@ -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, "</PRE>\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 <P><TT> */
           break;
 
-        case TEXT_ITEM_BLANK_LINE:
-          fputs ("<BR>", html->file);
-          break;
-
         case TEXT_ITEM_EJECT_PAGE:
           /* Nothing to do. */
           break;
index 19f7072dd0f23e49ef7d00b1c1e7b7a0a1b4ca66..df0a052234be98b1c89daf2167426c37557a5f5d 100644 (file)
@@ -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
index eb3155affc948926f32b093bec2278c4553ee4d9..7cd1ef3841c7f810459cc70c638f0eb2789612b6 100644 (file)
 #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 *
index 2e27529fecbae5ad29039bf4f07ea618c978ed26..9efc26490baa59c46459a9c4f72dc952902e0974 100644 (file)
@@ -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
   {
index 3a7ce4d357bb70b8e2a9e5a6f5e812fa33826b44..2f92b2696a6ec30f1c1abe7c23deeb2a4be5f3fb 100644 (file)
@@ -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;
index 60baac7e9c785af928ebbbc4488524008dba2e6b..d506a4d37f0522462d8a2a57ecc716509987888c 100644 (file)
@@ -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
index f3367f1b2e50883da524a4eccb494bc8ed12f71e..eb3cbe75a8ff215298333ca0207efabd29098f08 100644 (file)
@@ -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
 
index 02c18c619f2e3513268291e9c283ec2ea758be87..0e32361b718e45ee2c58d0c9ffd6f9f5379e0b90 100644 (file)
@@ -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