output-item: Make label a part of every output_item.
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 27 Dec 2020 20:12:13 +0000 (12:12 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Fri, 1 Jan 2021 19:15:05 +0000 (11:15 -0800)
I didn't understand before that every output_item had a label of its own
that users could separately edit.  The spv file format makes that clear,
though, so this commit acknowledges it.

27 files changed:
src/language/command.c
src/language/data-io/print-space.c
src/language/data-io/print.c
src/language/lexer/lexer.c
src/language/stats/reliability.c
src/language/utilities/echo.c
src/language/utilities/host.c
src/output/ascii.c
src/output/cairo-fsm.c
src/output/cairo-pager.c
src/output/chart-item.c
src/output/driver.c
src/output/group-item.c
src/output/group-item.h
src/output/message-item.c
src/output/output-item-provider.h
src/output/output-item.c
src/output/output-item.h
src/output/page-eject-item.c
src/output/page-setup-item.c
src/output/spv/spv-output.c
src/output/table-item.c
src/output/text-item.c
src/output/text-item.h
src/ui/gui/psppire-output-view.c
src/ui/gui/psppire-window.c
utilities/pspp-output.c

index 2fa05ad742d21ed6752a991ff6120893dfb2acea..de20cb3caac25833be6ffd60654c3b5ac48e8436 100644 (file)
@@ -199,7 +199,7 @@ do_parse_command (struct lexer *lexer,
       goto finish;
     }
   group_open_item_submit (group_open_item_create_nocopy (
-                            utf8_to_title (command->name)));
+                            utf8_to_title (command->name), NULL));
   opened = true;
 
   if (command->function == NULL)
index 555cdfa405031dde23a81f4e6f3a87a5643a5649..60eee4ce815bbcfb9f79815b7d575c3e44857dae 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_LOG, ""));
+      text_item_submit (text_item_create (TEXT_ITEM_LOG, "", NULL));
     else
       dfm_put_record (trns->writer, " ", 1); /* XXX */
 
index 4a767dc3dc9a4a3704b0cc876a0e907932e0b9e4..7111c03c65a6e98f42bab1407433e7f94d853ee1 100644 (file)
@@ -564,7 +564,8 @@ print_text_flush_records (struct print_trns *trns, struct u8_line *line,
 
       if (trns->writer == NULL)
         text_item_submit (text_item_create (TEXT_ITEM_LOG,
-                                            ds_cstr (&line->s) + 1));
+                                            ds_cstr (&line->s) + 1,
+                                            NULL));
       else
         {
           size_t len = ds_length (&line->s);
index 8d2468ab7bb31aae1e13e1eff902f0a2b88daa9f..820a48127175bd6decd0545d28d0f3f37f5533f7 100644 (file)
@@ -1444,7 +1444,8 @@ lex_source_get__ (const struct lex_source *src_)
 
       /* Submit the line as syntax. */
       text_item_submit (text_item_create_nocopy (TEXT_ITEM_SYNTAX,
-                                                 xmemdup0 (line, copy_len)));
+                                                 xmemdup0 (line, copy_len),
+                                                 NULL));
 
       src->journal_pos += line_len;
     }
index 7b73f8a47e7efbbcccac94cd515f5c4a5037a4e9..b8dc10b90f53562d35bd157493e8390e206ca704 100644 (file)
@@ -522,8 +522,10 @@ 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_TITLE, _("Scale: %s"),
-                                             ds_cstr (&rel->scale_name)));
+  text_item_submit (text_item_create_nocopy (
+                      TEXT_ITEM_TITLE,
+                      xasprintf (_("Scale: %s"), ds_cstr (&rel->scale_name)),
+                      NULL));
 
   case_processing_summary (n_valid, n_missing, dataset_dict (ds));
 }
index 3f31e335f68432f758ae8a2b75693cea9b114eee..861fcf88bbf292fa8ee5669a6f1a7f67b8b8adf9 100644 (file)
@@ -24,6 +24,9 @@
 
 #include "gl/xalloc.h"
 
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
 /* Echos a string to the output stream */
 int
 cmd_echo (struct lexer *lexer, struct dataset *ds UNUSED)
@@ -31,7 +34,8 @@ cmd_echo (struct lexer *lexer, struct dataset *ds UNUSED)
   if (!lex_force_string (lexer))
     return CMD_FAILURE;
 
-  text_item_submit (text_item_create (TEXT_ITEM_LOG, lex_tokcstr (lexer)));
+  text_item_submit (text_item_create (TEXT_ITEM_LOG, lex_tokcstr (lexer),
+                                      _("Echo")));
   lex_get (lexer);
 
   return CMD_SUCCESS;
index 6298db2d8b5e581dddefc9648a849e97db473918..9d19620f7cf7c1ec82f4186755131e79205ece6f 100644 (file)
@@ -261,7 +261,8 @@ run_command (const char *command, struct timespec timeout)
       if (end > output && end[-1] == '\n')
         end[-1] = '\0';
 
-      text_item_submit (text_item_create_nocopy (TEXT_ITEM_LOG, output));
+      text_item_submit (text_item_create_nocopy (TEXT_ITEM_LOG, output,
+                                                 xstrdup (_("Host Output"))));
     }
   free (locale_output);
 
index 29cc73b81115dcb1f5d0216c49919dcd93640abd..c058f23f8a645068ce33de1ca4eda607c339242c 100644 (file)
@@ -608,8 +608,10 @@ ascii_submit (struct output_driver *driver,
         {
           struct text_item *text_item;
 
-          text_item = text_item_create_format (
-            TEXT_ITEM_LOG, _("See %s for a chart."), file_name);
+          text_item = text_item_create_nocopy (
+            TEXT_ITEM_LOG,
+            xasprintf (_("See %s for a chart."), file_name),
+            NULL);
 
           ascii_submit (driver, &text_item->output_item);
           text_item_unref (text_item);
index 39f1cf02e990fd33ff345975f35daac2dd88aaff..708bedec03756fc379a193151066c31e2197104e 100644 (file)
@@ -992,7 +992,8 @@ xr_fsm_create (const struct output_item *item_,
       item = table_item_super (
         text_item_to_table_item (
           text_item_create (TEXT_ITEM_TITLE,
-                            to_group_open_item (item_)->command_name)));
+                            to_group_open_item (item_)->command_name,
+                            NULL)));
     }
   else
     NOT_REACHED ();
index 1ab481f0c3930f1108171a478fa1120592ec04a7..e87ec325248f2cff94d2fbdd2c57d7d1febc6a07 100644 (file)
@@ -451,27 +451,8 @@ xr_pager_run (struct xr_pager *p)
                     }
                   p->n_opens = 0;
 
-                  const char *text;
-                  if (is_table_item (p->item))
-                    {
-                      const struct table_item_text *title
-                        = table_item_get_title (to_table_item (p->item));
-                      text = title ? title->content : "Table";
-                    }
-                  else if (is_chart_item (p->item))
-                    {
-                      const char *title
-                        = chart_item_get_title (to_chart_item (p->item));
-                      text = title ? title : "Chart";
-                    }
-                  else
-                    text = (is_page_eject_item (p->item) ? "Page Break"
-                            : is_page_setup_item (p->item) ? "Page Setup"
-                            : is_message_item (p->item) ? "Message"
-                            : is_text_item (p->item) ? "Text"
-                            : NULL);
-                  if (text)
-                    add_outline (p->cr, parent_group_id, text, attrs, 0);
+                  add_outline (p->cr, parent_group_id,
+                               output_item_get_label (p->item), attrs, 0);
                   free (attrs);
                 }
               free (dest_name);
index f4b19a2f275c897abc36919c68cc3be725a77143..3b3761372e185af855992f5c5d3a3b162e497722 100644 (file)
@@ -29,6 +29,9 @@
 
 #include "gl/xalloc.h"
 
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
 /* Initializes ITEM as a chart item of the specified CLASS.  The new chart item
    initially has the specified TITLE, which may be NULL if no title is yet
    available.  The caller retains ownership of TITLE.
@@ -40,9 +43,11 @@ void
 chart_item_init (struct chart_item *item, const struct chart_item_class *class,
                  const char *title)
 {
-  output_item_init (&item->output_item, &chart_item_class);
-  item->class = class;
-  item->title = title != NULL ? xstrdup (title) : NULL;
+  *item = (struct chart_item) {
+    .output_item = OUTPUT_ITEM_INITIALIZER (&chart_item_class),
+    .class = class,
+    .title = title ? xstrdup (title) : NULL
+  };
 }
 
 /* Returns ITEM's title, which is a null pointer if no title has been set. */
@@ -73,6 +78,13 @@ chart_item_submit (struct chart_item *item)
   output_submit (&item->output_item);
 }
 \f
+static const char *
+chart_item_get_label (const struct output_item *output_item)
+{
+  const struct chart_item *item = to_chart_item (output_item);
+  return item->title ? item->title : _("Chart");
+}
+
 static void
 chart_item_destroy (struct output_item *output_item)
 {
@@ -84,6 +96,6 @@ chart_item_destroy (struct output_item *output_item)
 
 const struct output_item_class chart_item_class =
   {
-    "chart",
+    chart_item_get_label,
     chart_item_destroy,
   };
index cf0a535f3c059ce6ae9b05a084b76ea94925265c..be60cb2746f4d0dc96b3ae6ee9ea5fc8e4c162e9 100644 (file)
@@ -54,8 +54,7 @@ struct output_engine
   {
     struct ll ll;                  /* Node for this engine. */
     struct llx_list drivers;       /* Contains "struct output_driver"s. */
-    struct string deferred_text;   /* Output text being accumulated. */
-    enum text_item_type deferred_type; /* Type of text being accumulated. */
+    struct text_item *deferred_text;   /* Output text being accumulated. */
     char *command_name;            /* Name of command being processed. */
     char *title, *subtitle;        /* Components of page title. */
 
@@ -101,7 +100,6 @@ output_engine_push (void)
   struct output_engine *e = xzalloc (sizeof (*e));
 
   llx_init (&e->drivers);
-  ds_init_empty (&e->deferred_text);
 
   string_map_init (&e->heading_vars);
 
@@ -124,7 +122,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_text);
+  text_item_unref (e->deferred_text);
   free (e->command_name);
   free (e->title);
   free (e->subtitle);
@@ -180,40 +178,30 @@ output_submit__ (struct output_engine *e, struct output_item *item)
 static void
 flush_deferred_text (struct output_engine *e)
 {
-  if (!ds_is_empty (&e->deferred_text))
+  struct text_item *deferred_text = e->deferred_text;
+  if (deferred_text)
     {
-      char *text = ds_steal_cstr (&e->deferred_text);
-      output_submit__ (e, text_item_super (text_item_create_nocopy (
-                                             e->deferred_type, text)));
+      e->deferred_text = NULL;
+      output_submit__ (e, text_item_super (deferred_text));
     }
 }
 
 static bool
-defer_text (struct output_engine *e, struct output_item *item)
+defer_text (struct output_engine *e, struct output_item *output_item)
 {
-  if (!is_text_item (item))
+  if (!is_text_item (output_item))
     return false;
 
-  struct text_item *text_item = to_text_item (item);
-  if (text_item->markup)        /* XXX */
-    return false;
-
-  enum text_item_type type = text_item_get_type (text_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 (text_item);
-  ds_put_cstr (&e->deferred_text, text);
-  output_item_unref (item);
-
+  struct text_item *text = to_text_item (output_item);
+  if (!e->deferred_text)
+    e->deferred_text = text_item_unshare (text);
+  else if (text_item_append (e->deferred_text, text))
+    text_item_unref (text);
+  else
+    {
+      flush_deferred_text (e);
+      e->deferred_text = text_item_unshare (text);
+    }
   return true;
 }
 
@@ -326,7 +314,7 @@ output_set_title__ (struct output_engine *e, char **dst, const char *src)
        : e->subtitle ? xstrdup (e->subtitle)
        : xzalloc (1));
   text_item_submit (text_item_create_nocopy (TEXT_ITEM_PAGE_TITLE,
-                                             page_title));
+                                             page_title, NULL));
 }
 
 void
index 34ca63eab33858c84717f159b4ede4c67e04bf6a..3bfb44cd096146d9b97af5c9958d9741fc457296 100644 (file)
 
 #include <stdlib.h>
 
+#include "libpspp/compiler.h"
 #include "output/driver.h"
 #include "output/output-item-provider.h"
 
 #include "gl/xalloc.h"
 
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
 struct group_open_item *
-group_open_item_create (const char *command_name)
+group_open_item_create (const char *command_name, const char *label)
 {
   return group_open_item_create_nocopy (
-    command_name ? xstrdup (command_name) : NULL);
+    command_name ? xstrdup (command_name) : NULL,
+    label ? xstrdup (label) : NULL);
 }
 
 struct group_open_item *
-group_open_item_create_nocopy (char *command_name)
+group_open_item_create_nocopy (char *command_name, char *label)
 {
-  struct group_open_item *item;
-
-  item = xmalloc (sizeof *item);
-  output_item_init (&item->output_item, &group_open_item_class);
-  item->command_name = command_name;
-
+  struct group_open_item *item = xmalloc (sizeof *item);
+  *item = (struct group_open_item) {
+    .output_item = OUTPUT_ITEM_INITIALIZER (&group_open_item_class),
+    .output_item.label = label,
+    .command_name = command_name,
+  };
   return item;
 }
 
@@ -52,6 +57,14 @@ group_open_item_submit (struct group_open_item *item)
   output_submit (&item->output_item);
 }
 
+static const char *
+group_open_item_get_label (const struct output_item *output_item)
+{
+  struct group_open_item *item = to_group_open_item (output_item);
+
+  return item->command_name ? item->command_name : _("Group");
+}
+
 static void
 group_open_item_destroy (struct output_item *output_item)
 {
@@ -63,18 +76,17 @@ group_open_item_destroy (struct output_item *output_item)
 
 const struct output_item_class group_open_item_class =
   {
-    "group_open",
+    group_open_item_get_label,
     group_open_item_destroy,
   };
 \f
 struct group_close_item *
 group_close_item_create (void)
 {
-  struct group_close_item *item;
-
-  item = xmalloc (sizeof *item);
-  output_item_init (&item->output_item, &group_close_item_class);
-
+  struct group_close_item *item = xmalloc (sizeof *item);
+  *item = (struct group_close_item) {
+    .output_item = OUTPUT_ITEM_INITIALIZER (&group_close_item_class),
+  };
   return item;
 }
 
@@ -86,6 +98,13 @@ group_close_item_submit (struct group_close_item *item)
   output_submit (&item->output_item);
 }
 
+static const char *
+group_close_item_get_label (const struct output_item *output_item UNUSED)
+{
+  /* Not marked for translation: user should never see it. */
+  return "Group Close";
+}
+
 static void
 group_close_item_destroy (struct output_item *output_item)
 {
@@ -96,6 +115,6 @@ group_close_item_destroy (struct output_item *output_item)
 
 const struct output_item_class group_close_item_class =
   {
-    "group_close",
+    group_close_item_get_label,
     group_close_item_destroy,
   };
index 3633b14bf584ac2dcc00e5003387079b946a58b3..61593632cd513990a7b10f302f750db0f950cffc 100644 (file)
 struct group_open_item
   {
     struct output_item output_item;
+
+    /* Locale-invariant name of the command that produced the enclosed output.
+       May be NULL if the group doesn't enclose a particular command's
+       output. */
     char *command_name;
   };
 
-struct group_open_item *group_open_item_create (const char *command_name);
-struct group_open_item *group_open_item_create_nocopy (char *command_name);
+struct group_open_item *group_open_item_create (const char *command_name,
+                                                const char *label);
+struct group_open_item *group_open_item_create_nocopy (char *command_name,
+                                                       char *label);
 
 /* A group_close item. */
 struct group_close_item
index ddfcc3f42ee33d26099d4121f8fd5c6fe856f756..e834d5a7b2d10626f150f5f944a3647176e395f4 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <stdlib.h>
 
+#include "libpspp/i18n.h"
 #include "libpspp/message.h"
 #include "output/driver.h"
 #include "output/output-item-provider.h"
 
 #include "gl/xalloc.h"
 
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
 struct message_item *
 message_item_create (const struct msg *msg)
 {
-  struct message_item *item;
-
-  item = xmalloc (sizeof *msg);
-  output_item_init (&item->output_item, &message_item_class);
-  item->msg = msg_dup (msg);
-
+  struct message_item *item = xmalloc (sizeof *msg);
+  *item = (struct message_item) {
+    .output_item = OUTPUT_ITEM_INITIALIZER (&message_item_class),
+    .msg = msg_dup (msg)
+  };
   return item;
 }
 
@@ -49,11 +52,22 @@ struct text_item *
 message_item_to_text_item (struct message_item *message_item)
 {
   struct text_item *text_item = text_item_create_nocopy (
-    TEXT_ITEM_LOG, msg_to_string (message_item_get_msg (message_item)));
+    TEXT_ITEM_LOG,
+    msg_to_string (message_item_get_msg (message_item)),
+    xstrdup (output_item_get_label (message_item_super (message_item))));
   message_item_unref (message_item);
   return text_item;
 }
 
+static const char *
+message_item_get_label (const struct output_item *output_item)
+{
+  const struct message_item *item = to_message_item (output_item);
+  return (item->msg->severity == MSG_S_ERROR ? _("Error")
+          : item->msg->severity == MSG_S_WARNING ? _("Warning")
+          : _("Note"));
+}
+
 static void
 message_item_destroy (struct output_item *output_item)
 {
@@ -72,6 +86,6 @@ message_item_submit (struct message_item *item)
 
 const struct output_item_class message_item_class =
   {
-    "message",
+    message_item_get_label,
     message_item_destroy,
   };
index aa2584bef4006d0221535ffbbd6d1f837eba5c04..7517996119112faa2d683dde6d6522d01b492595 100644 (file)
    instance of output_item. */
 struct output_item_class
   {
-    const char *name;
+    /* Returns the localized label to use for ITEM.  This is only called when
+       ITEM does not have an explicitly set label, that is, when 'item->label'
+       is NULL. */
+    const char *(*get_label) (const struct output_item *item);
 
     /* Destroys and frees ITEM.  Called when output_item_unref() drops ITEM's
        reference count to 0. */
     void (*destroy) (struct output_item *item);
   };
 
-void output_item_init (struct output_item *, const struct output_item_class *);
+#define OUTPUT_ITEM_INITIALIZER(CLASS) { .class = CLASS, .ref_cnt = 1 }
+#define OUTPUT_ITEM_CLONE_INITIALIZER(SRC)                      \
+  {                                                             \
+    .class = (SRC)->class,                                      \
+    .ref_cnt = 1,                                               \
+    .label = (SRC)->label ? xstrdup ((SRC)->label) : NULL,      \
+  }
 
 #endif /* output/output-item-provider.h */
index dd4e70c2ee48b8591813767374d166b8a1615b0e..860678b25f99932e3949fa23bd7cc404f3e2b164 100644 (file)
@@ -25,6 +25,8 @@
 #include "libpspp/cast.h"
 
 #include "gl/xalloc.h"
+
+#include "gettext.h"
 \f
 /* Increases ITEM's reference count, indicating that it has an additional
    owner.  An output item that is shared among multiple owners must not be
@@ -46,7 +48,11 @@ output_item_unref (struct output_item *item)
     {
       assert (item->ref_cnt > 0);
       if (--item->ref_cnt == 0)
-        item->class->destroy (item);
+        {
+          char *label = item->label;
+          item->class->destroy (item);
+          free (label);
+        }
     }
 }
 
@@ -57,17 +63,34 @@ output_item_is_shared (const struct output_item *item)
 {
   return item->ref_cnt > 1;
 }
-\f
-/* Initializes ITEM as an output item of the specified CLASS, initially with a
-   reference count of 1.
 
-   An output item is an abstract class, that is, a plain output_item is not
-   useful on its own.  Thus, this function is normally called from the
-   initialization function of some subclass of output_item. */
+/* Returns the label for ITEM, which the caller must not modify or free. */
+const char *
+output_item_get_label (const struct output_item *item)
+{
+  return item->label ? item->label : item->class->get_label (item);
+}
+
+/* Sets the label for ITEM to LABEL.  The caller retains ownership of LABEL.
+   If LABEL is nonnull, it overrides any previously set label and the default
+   label.  If LABEL is null, ITEM will now use its default label.
+
+   ITEM must not be shared. */
+void
+output_item_set_label (struct output_item *item, const char *label)
+{
+  output_item_set_label_nocopy (item, label ? xstrdup (label) : NULL);
+}
+
+/* Sets the label for ITEM to LABEL, transferring ownership of LABEL to ITEM.
+   If LABEL is nonnull, it overrides any previously set label and the default
+   label.  If LABEL is null, ITEM will now use its default label.
+
+   ITEM must not be shared. */
 void
-output_item_init (struct output_item *item,
-                  const struct output_item_class *class)
+output_item_set_label_nocopy (struct output_item *item, char *label)
 {
-  item->class = class;
-  item->ref_cnt = 1;
+  assert (!output_item_is_shared (item));
+  free (item->label);
+  item->label = label;
 }
index 13a5c34a74348d3361653d8b3842071b033a2097..b522df4c90d042f46fcb1c1c95025e0bcaaaa515 100644 (file)
 
 /* Output items.
 
-   An output item is a self-contained chunk of output.  The
-   following kinds of output items currently exist:
-
-        - Tables (see output/table-item.h).
-
-        - Charts (see output/chart-item.h).
-
-        - Text strings (see output/text-item.h).
-
-        - Messages (see output/message-item.h).
+   An output item is a self-contained chunk of output.  Several kinds of output
+   items exist.  See *-item.h for details.
 */
 
 #include <stdbool.h>
@@ -43,10 +35,21 @@ struct output_item
        indicated by a reference count greater than 1.  When this is the case,
        the output item must not be modified. */
     int ref_cnt;
+
+    /* The localized label for the item that appears in the outline pane in the
+       PSPPIRE output viewer and in PDF outlines.  This is NULL if no label has
+       been explicitly set.
+
+       Use output_item_get_label() to read an item's label. */
+    char *label;
   };
 
 struct output_item *output_item_ref (const struct output_item *);
 void output_item_unref (struct output_item *);
 bool output_item_is_shared (const struct output_item *);
 
+const char *output_item_get_label (const struct output_item *);
+void output_item_set_label (struct output_item *, const char *);
+void output_item_set_label_nocopy (struct output_item *, char *);
+
 #endif /* output/output-item.h */
index 5d108f989072f06463d03b280bf77648d0764d27..85bde8c95926edabe5e222f346549828c7f88bc9 100644 (file)
 
 #include "gl/xalloc.h"
 
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
 struct page_eject_item *
 page_eject_item_create (void)
 {
   struct page_eject_item *item = xmalloc (sizeof *item);
-  output_item_init (&item->output_item, &page_eject_item_class);
+  *item = (struct page_eject_item) {
+    .output_item = OUTPUT_ITEM_INITIALIZER (&page_eject_item_class),
+  };
   return item;
 }
 
@@ -41,6 +46,12 @@ page_eject_item_submit (struct page_eject_item *item)
   output_submit (&item->output_item);
 }
 
+static const char *
+page_eject_item_get_label (const struct output_item *output_item UNUSED)
+{
+  return _("Page Break");
+}
+
 static void
 page_eject_item_destroy (struct output_item *output_item)
 {
@@ -49,6 +60,6 @@ page_eject_item_destroy (struct output_item *output_item)
 
 const struct output_item_class page_eject_item_class =
   {
-    "page_eject",
+    page_eject_item_get_label,
     page_eject_item_destroy,
   };
index 45cf8c977f0d8c48456e67c6ebb9c678e2da35a6..7be1c2c43b66eeb36ec018f3aea12ff8938b588b 100644 (file)
@@ -102,8 +102,10 @@ struct page_setup_item *
 page_setup_item_create (const struct page_setup *ps)
 {
   struct page_setup_item *item = xmalloc (sizeof *item);
-  output_item_init (&item->output_item, &page_setup_item_class);
-  item->page_setup = page_setup_clone (ps);
+  *item = (struct page_setup_item) {
+    .output_item = OUTPUT_ITEM_INITIALIZER (&page_setup_item_class),
+    .page_setup = page_setup_clone (ps),
+  };
   return item;
 }
 
@@ -115,6 +117,13 @@ page_setup_item_submit (struct page_setup_item *item)
   output_submit (&item->output_item);
 }
 
+static const char *
+page_setup_item_get_label (const struct output_item *output_item UNUSED)
+{
+  /* Not marked for translation: user should never see it. */
+  return "Page Setup";
+}
+
 static void
 page_setup_item_destroy (struct output_item *output_item)
 {
@@ -125,6 +134,6 @@ page_setup_item_destroy (struct output_item *output_item)
 
 const struct output_item_class page_setup_item_class =
   {
-    "page_setup",
+    page_setup_item_get_label,
     page_setup_item_destroy,
   };
index f83ff4542b2f0627c90c03ecea2b04b19d54c0d5..cb4964a7f1d7bdf9d4c33b6fd661e366769684dd 100644 (file)
@@ -35,7 +35,8 @@ spv_text_submit (const struct spv_item *in)
   const struct pivot_value *value = spv_item_get_text (in);
   char *text = pivot_value_to_string (value, SETTINGS_VALUE_SHOW_DEFAULT,
                                       SETTINGS_VALUE_SHOW_DEFAULT);
-  struct text_item *item = text_item_create_nocopy (type, text);
+  char *label = in->label ? xstrdup (in->label) : NULL;
+  struct text_item *item = text_item_create_nocopy (type, text, label);
   const struct font_style *font = value->font_style;
   if (font)
     {
index 9355a77a115f58f20462207c71e142d97ab313a9..1b17c320a6e8e5a119c5ac1ff70a79089dcd83c6 100644 (file)
@@ -29,6 +29,9 @@
 
 #include "gl/xalloc.h"
 
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
 struct table_item_text *
 table_item_text_create (const char *content)
 {
@@ -78,7 +81,7 @@ table_item_layer_copy (struct table_item_layer *dst,
                             src->n_footnotes * sizeof *src->footnotes);
   dst->n_footnotes = src->n_footnotes;
 }
-
 void
 table_item_layer_uninit (struct table_item_layer *layer)
 {
@@ -127,13 +130,13 @@ table_item_create (struct table *table, const char *title, const char *caption,
                    const char *notes)
 {
   struct table_item *item = xmalloc (sizeof *item);
-  output_item_init (&item->output_item, &table_item_class);
-  item->table = table;
-  item->title = table_item_text_create (title);
-  item->layers = NULL;
-  item->caption = table_item_text_create (caption);
-  item->notes = notes ? xstrdup (notes) : NULL;
-  item->pt = NULL;
+  *item = (struct table_item) {
+    .output_item = OUTPUT_ITEM_INITIALIZER (&table_item_class),
+    .table = table,
+    .title = table_item_text_create (title),
+    .caption = table_item_text_create (caption),
+    .notes = notes ? xstrdup (notes) : NULL,
+  };
   return item;
 }
 
@@ -238,6 +241,15 @@ table_item_submit (struct table_item *table_item)
   output_submit (&table_item->output_item);
 }
 \f
+static const char *
+table_item_get_label (const struct output_item *output_item)
+{
+  const struct table_item *item = to_table_item (output_item);
+  return (item->title && item->title->content
+          ? item->title->content
+          : _("Table"));
+}
+
 static void
 table_item_destroy (struct output_item *output_item)
 {
@@ -253,6 +265,6 @@ table_item_destroy (struct output_item *output_item)
 
 const struct output_item_class table_item_class =
   {
-    "table",
+    table_item_get_label,
     table_item_destroy,
   };
index 8c7f0dc51a67afe97ebdd1f09d2c9424e0673454..55319e9ef676c68c3ff5dedfcc319879f9f2f1d3 100644 (file)
@@ -34,6 +34,7 @@
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
 
 const char *
 text_item_type_to_string (enum text_item_type type)
@@ -55,40 +56,31 @@ text_item_type_to_string (enum text_item_type type)
     }
 }
 
-/* Creates and returns a new text item containing TEXT and the specified TYPE.
-   The new text item takes ownership of TEXT. */
+/* Creates and returns a new text item containing TEXT and the specified TYPE
+   and LABEL.  The new text item takes ownership of TEXT and LABEL.  If LABEL
+   is NULL, uses the default label for TYPE. */
 struct text_item *
-text_item_create_nocopy (enum text_item_type type, char *text)
+text_item_create_nocopy (enum text_item_type type, char *text, char *label)
 {
   struct text_item *item = xzalloc (sizeof *item);
-  output_item_init (&item->output_item, &text_item_class);
-  item->text = text;
-  item->type = type;
+  *item = (struct text_item) {
+    .output_item = OUTPUT_ITEM_INITIALIZER (&text_item_class),
+    .output_item.label = label,
+    .text = text,
+    .type = type,
+  };
   return item;
 }
 
 /* Creates and returns a new text item containing a copy of TEXT and the
-   specified TYPE.  The caller retains ownership of TEXT. */
-struct text_item *
-text_item_create (enum text_item_type type, const char *text)
-{
-  return text_item_create_nocopy (type, xstrdup (text));
-}
-
-/* Creates and returns a new text item containing a copy of FORMAT, which is
-   formatted as if by printf(), and the specified TYPE.  The caller retains
-   ownership of FORMAT. */
+   specified TYPE and LABEL.  The caller retains ownership of TEXT and LABEL.
+   If LABEL is null, uses a default label for TYPE. */
 struct text_item *
-text_item_create_format (enum text_item_type type, const char *format, ...)
+text_item_create (enum text_item_type type, const char *text,
+                  const char *label)
 {
-  struct text_item *item;
-  va_list args;
-
-  va_start (args, format);
-  item = text_item_create_nocopy (type, xvasprintf (format, args));
-  va_end (args);
-
-  return item;
+  return text_item_create_nocopy (type, xstrdup (text),
+                                  label ? xstrdup (label) : NULL);
 }
 
 /* Returns ITEM's type. */
@@ -113,6 +105,62 @@ text_item_submit (struct text_item *item)
   output_submit (&item->output_item);
 }
 
+struct text_item *
+text_item_unshare (struct text_item *old)
+{
+  assert (old->output_item.ref_cnt > 0);
+  if (!text_item_is_shared (old))
+    return old;
+  text_item_unref (old);
+
+  struct text_item *new = xmalloc (sizeof *new);
+  *new = (struct text_item) {
+    .output_item = OUTPUT_ITEM_CLONE_INITIALIZER (&old->output_item),
+    .text = xstrdup (old->text),
+    .type = old->type,
+    .bold = old->bold,
+    .italic = old->italic,
+    .underline = old->underline,
+    .markup = old->markup,
+    .typeface = old->typeface ? xstrdup (old->typeface) : NULL,
+    .size = old->size
+  };
+  return new;
+}
+
+/* Attempts to append the text in SRC to DST.  If successful, returns true,
+   otherwise false.
+
+   Only TEXT_ITEM_SYNTAX and TEXT_ITEM_LOG items can be combined, and not with
+   each other.
+
+   DST must not be shared. */
+bool
+text_item_append (struct text_item *dst, const struct text_item *src)
+{
+  assert (!text_item_is_shared (dst));
+  if (dst->type != src->type
+      || (dst->type != TEXT_ITEM_SYNTAX && dst->type != TEXT_ITEM_LOG)
+      || strcmp (output_item_get_label (&dst->output_item),
+                 output_item_get_label (&src->output_item))
+      || dst->bold != src->bold
+      || dst->italic != src->italic
+      || dst->underline != src->underline
+      || dst->markup
+      || src->markup
+      || strcmp (dst->typeface ? dst->typeface : "",
+                 src->typeface ? src->typeface : "")
+      || dst->size != src->size)
+    return false;
+  else
+    {
+      char *new_text = xasprintf ("%s\n%s", dst->text, src->text);
+      free (dst->text);
+      dst->text = new_text;
+      return true;
+    }
+}
+
 struct table_item *
 text_item_to_table_item (struct text_item *text_item)
 {
@@ -142,6 +190,13 @@ text_item_to_table_item (struct text_item *text_item)
   return table_item;
 }
 \f
+static const char *
+text_item_get_label (const struct output_item *output_item)
+{
+  const struct text_item *item = to_text_item (output_item);
+  return text_item_type_to_string (item->type);
+}
+
 static void
 text_item_destroy (struct output_item *output_item)
 {
@@ -153,6 +208,6 @@ text_item_destroy (struct output_item *output_item)
 
 const struct output_item_class text_item_class =
   {
-    "text",
+    text_item_get_label,
     text_item_destroy,
   };
index 554d478f7407df186d726df8ae6c497db0e456d4..28e2457a2c9f8c76491b5243ee259108aeb745c2 100644 (file)
@@ -50,15 +50,17 @@ struct text_item
     int size;
   };
 
-struct text_item *text_item_create (enum text_item_type, const char *text);
-struct text_item *text_item_create_nocopy (enum text_item_type, char *text);
-struct text_item *text_item_create_format (enum text_item_type,
-                                           const char *format, ...)
-  PRINTF_FORMAT (2, 3);
+struct text_item *text_item_create (enum text_item_type,
+                                    const char *text, const char *label);
+struct text_item *text_item_create_nocopy (enum text_item_type,
+                                           char *text, char *label);
 
 enum text_item_type text_item_get_type (const struct text_item *);
 const char *text_item_get_text (const struct text_item *);
 
+struct text_item *text_item_unshare (struct text_item *);
+bool text_item_append (struct text_item *dst, const struct text_item *src);
+
 struct table_item *text_item_to_table_item (struct text_item *);
 \f
 /* This boilerplate for text_item, a subclass of output_item, was
index 3e9151f6089944eedc733f6c10623417a1ce2f6d..cc00a05bdaa327f8188f43bdeb826bc311daa00c 100644 (file)
@@ -84,9 +84,9 @@ struct psppire_output_view
 
 enum
   {
-    COL_NAME,                   /* Table name. */
+    COL_LABEL,                  /* Output item label. */
     COL_ADDR,                   /* Pointer to the table */
-    COL_Y,                      /* Y position of top of name. */
+    COL_Y,                      /* Y position of top of object. */
     N_COLS
   };
 
@@ -405,7 +405,6 @@ psppire_output_view_put (struct psppire_output_view *view,
 {
   struct output_view_item *view_item;
   GtkWidget *drawing_area;
-  struct string name;
   int tw, th;
 
   if (is_group_close_item (item))
@@ -473,8 +472,6 @@ psppire_output_view_put (struct psppire_output_view *view,
       GtkTreeStore *store = GTK_TREE_STORE (
         gtk_tree_view_get_model (view->overview));
 
-      ds_init_empty (&name);
-
       /* Create a new node in the tree and puts a reference to it in 'iter'. */
       GtkTreeIter iter;
       GtkTreeIter parent;
@@ -493,45 +490,11 @@ psppire_output_view_put (struct psppire_output_view *view,
                                                      &iter);
         }
 
-      ds_clear (&name);
-      if (is_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);
-          const struct msg *msg = message_item_get_msg (msg_item);
-          ds_put_format (&name, "%s: %s", _("Message"),
-                         msg_severity_to_string (msg->severity));
-        }
-      else if (is_table_item (item))
-        {
-          const struct table_item_text *title
-            = table_item_get_title (to_table_item (item));
-          if (title != NULL)
-            ds_put_format (&name, "Table: %s", title->content);
-          else
-            ds_put_cstr (&name, "Table");
-        }
-      else if (is_chart_item (item))
-        {
-          const char *s = chart_item_get_title (to_chart_item (item));
-          if (s != NULL)
-            ds_put_format (&name, "Chart: %s", s);
-          else
-            ds_put_cstr (&name, "Chart");
-        }
-      else if (is_group_open_item (item))
-        ds_put_cstr (&name, to_group_open_item (item)->command_name);
       gtk_tree_store_set (store, &iter,
-                          COL_NAME, ds_cstr (&name),
+                          COL_LABEL, output_item_get_label (item),
                          COL_ADDR, item,
-                          COL_Y, (view->y),
+                          COL_Y, view->y,
                           -1);
-      ds_destroy (&name);
 
       GtkTreePath *path = gtk_tree_model_get_path (
         GTK_TREE_MODEL (store), &iter);
@@ -877,7 +840,7 @@ psppire_output_view_new (GtkLayout *output, GtkTreeView *overview)
 
       model = GTK_TREE_MODEL (gtk_tree_store_new (
                                 N_COLS,
-                                G_TYPE_STRING,  /* COL_NAME */
+                                G_TYPE_STRING,  /* COL_LABEL */
                                 G_TYPE_POINTER, /* COL_ADDR */
                                 G_TYPE_LONG));  /* COL_Y */
       gtk_tree_view_set_model (overview, model);
@@ -887,7 +850,7 @@ psppire_output_view_new (GtkLayout *output, GtkTreeView *overview)
       gtk_tree_view_append_column (GTK_TREE_VIEW (overview), column);
       renderer = gtk_cell_renderer_text_new ();
       gtk_tree_view_column_pack_start (column, renderer, TRUE);
-      gtk_tree_view_column_add_attribute (column, renderer, "text", COL_NAME);
+      gtk_tree_view_column_add_attribute (column, renderer, "text", COL_LABEL);
 
       g_signal_connect (GTK_TREE_VIEW (overview),
                         "row-activated", G_CALLBACK (on_row_activate), view);
index 99da30fa3eac4772b15983a705c603a474dbb697..fe4119dd374fd8d40626693eb7184c791ff5ec16 100644 (file)
@@ -766,7 +766,8 @@ dump_heading_transition (const struct spv_item *old,
     group_close_item_submit (group_close_item_create ());
   for (size_t i = common; i < new_path.n; i++)
     group_open_item_submit (group_open_item_create (
-                              new_path.nodes[i]->command_id));
+                              new_path.nodes[i]->command_id,
+                              new_path.nodes[i]->label));
 
   free_path (&old_path);
   free_path (&new_path);
index 59c915dd443935e6eda189dfd6b6c12f76460adc..deb2000432e74e6e3a1c605fa3083866dacd4a14 100644 (file)
@@ -92,10 +92,14 @@ dump_item (const struct spv_item *item)
     {
       const char *x = item->xml_member;
       const char *b = item->bin_member;
+
+      /* The strings below are not marked for translation because they are only
+         useful to developers. */
       char *s = (x && b
-                 ? xasprintf (_("%s and %s:"), x, b)
+                 ? xasprintf ("%s and %s:", x, b)
                  : xasprintf ("%s:", x ? x : b));
-      text_item_submit (text_item_create_nocopy (TEXT_ITEM_TITLE, s));
+      text_item_submit (text_item_create_nocopy (TEXT_ITEM_TITLE, s,
+                                                 xstrdup ("Member Names")));
     }
 
   switch (spv_item_get_type (item))
@@ -268,7 +272,8 @@ dump_heading_transition (const struct spv_item *old,
     group_close_item_submit (group_close_item_create ());
   for (size_t i = common; i < new_path.n; i++)
     group_open_item_submit (group_open_item_create (
-                              new_path.nodes[i]->command_id));
+                              new_path.nodes[i]->command_id,
+                              new_path.nodes[i]->label));
 
   free_path (&old_path);
   free_path (&new_path);