table-item: Generalize support for layer info.
authorBen Pfaff <blp@cs.stanford.edu>
Sat, 19 Oct 2019 05:20:02 +0000 (05:20 +0000)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 29 Dec 2019 05:28:10 +0000 (05:28 +0000)
This adds support for an arbitrary number of layer text items and allows
the layer text items to have footnotes.

src/output/html.c
src/output/odt.c
src/output/pivot-output.c
src/output/render.c
src/output/table-item.c
src/output/table-item.h
src/output/table.c

index 806e6320145dbfbdd4efc4229bc0a033393a0c95..307f6c223d83bb3fea6040da6e7f04d8d30ada13 100644 (file)
@@ -419,6 +419,22 @@ html_put_table_item_text (struct html_driver *html,
   html_put_footnote_markers (html, text->footnotes, text->n_footnotes);
 }
 
+static void
+html_put_table_item_layers (struct html_driver *html,
+                            const struct table_item_layers *layers)
+{
+  for (size_t i = 0; i < layers->n_layers; i++)
+    {
+      if (i)
+        fputs ("<BR>\n", html->file);
+
+      const struct table_item_layer *layer = &layers->layers[i];
+      escape_string (html->file, layer->content, strlen (layer->content),
+                     " ", "<BR>");
+      html_put_footnote_markers (html, layer->footnotes, layer->n_footnotes);
+    }
+}
+
 static void
 html_output_table (struct html_driver *html, const struct table_item *item)
 {
@@ -454,7 +470,7 @@ html_output_table (struct html_driver *html, const struct table_item *item)
   fputs ("<TBODY VALIGN=\"TOP\">\n", html->file);
 
   const struct table_item_text *title = table_item_get_title (item);
-  const struct table_item_text *layers = table_item_get_layers (item);
+  const struct table_item_layers *layers = table_item_get_layers (item);
   if (title || layers)
     {
       fputs ("  <CAPTION>", html->file);
@@ -463,7 +479,7 @@ html_output_table (struct html_driver *html, const struct table_item *item)
       if (title && layers)
         fputs ("<BR>\n", html->file);
       if (layers)
-        html_put_table_item_text (html, layers);
+        html_put_table_item_layers (html, layers);
       fputs ("</CAPTION>\n", html->file);
     }
 
index a3f945421f00e351fc8e84a08a56f435dc7d95b1..104e57a9332c373502794a9550ddda5bc1b09f4d 100644 (file)
@@ -438,6 +438,26 @@ write_table_item_text (struct odt_driver *odt,
   xmlTextWriterEndElement (odt->content_wtr);
 }
 
+static void
+write_table_item_layers (struct odt_driver *odt,
+                         const struct table_item_layers *layers)
+{
+  if (!layers)
+    return;
+
+  for (size_t i = 0; i < layers->n_layers; i++)
+    {
+      const struct table_item_layer *layer = &layers->layers[i];
+      xmlTextWriterStartElement (odt->content_wtr, _xml("text:h"));
+      xmlTextWriterWriteFormatAttribute (odt->content_wtr,
+                                         _xml("text:outline-level"), "%d", 2);
+      xmlTextWriterWriteString (odt->content_wtr, _xml (layer->content) );
+      for (size_t i = 0; i < layer->n_footnotes; i++)
+        write_footnote (odt, layer->footnotes[i]);
+      xmlTextWriterEndElement (odt->content_wtr);
+    }
+}
+
 static void
 write_table (struct odt_driver *odt, const struct table_item *item)
 {
@@ -446,7 +466,7 @@ write_table (struct odt_driver *odt, const struct table_item *item)
 
   /* Write a heading for the table */
   write_table_item_text (odt, table_item_get_title (item));
-  write_table_item_text (odt, table_item_get_layers (item));
+  write_table_item_layers (odt, table_item_get_layers (item));
 
   /* Start table */
   xmlTextWriterStartElement (odt->content_wtr, _xml("table:table"));
index f9c22b7785131fdbbf9d59b693fd33d83bb6323c..d805f42bb36e87bb77b4c0d15a4e9b289b18c719 100644 (file)
@@ -299,24 +299,6 @@ pivot_table_submit_layer (const struct pivot_table *pt,
   const size_t *pindexes[PIVOT_N_AXES]
     = { [PIVOT_AXIS_LAYER] = layer_indexes };
 
-  struct string layer_label = DS_EMPTY_INITIALIZER;
-  const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
-  for (size_t i = 0; i < layer_axis->n_dimensions; i++)
-    {
-      const struct pivot_dimension *d = layer_axis->dimensions[i];
-      if (d->n_leaves)
-        {
-          if (!ds_is_empty (&layer_label))
-            ds_put_byte (&layer_label, '\n');
-          pivot_value_format (d->root->name, pt->show_values,
-                              pt->show_variables, &layer_label);
-          ds_put_cstr (&layer_label, ": ");
-          pivot_value_format (d->data_leaves[layer_indexes[i]]->name,
-                              pt->show_values, pt->show_variables,
-                              &layer_label);
-        }
-    }
-
   size_t body[TABLE_N_AXES];
   size_t *column_enumeration = pivot_table_enumerate_axis (
     pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->omit_empty, &body[H]);
@@ -462,16 +444,40 @@ pivot_table_submit_layer (const struct pivot_table *pt,
       table_item_text_destroy (title);
     }
 
-  if (!ds_is_empty (&layer_label))
+  const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
+  struct table_item_layers *layers = NULL;
+  for (size_t i = 0; i < layer_axis->n_dimensions; i++)
     {
-      struct table_item_text *layers = table_item_text_create (
-        ds_cstr (&layer_label));
-      layers->style = area_style_override (NULL, &pt->areas[PIVOT_AREA_LAYERS],
-                                           NULL, NULL);
-      table_item_set_layers (ti, layers);
-      table_item_text_destroy (layers);
+      const struct pivot_dimension *d = layer_axis->dimensions[i];
+      if (d->n_leaves)
+        {
+          if (!layers)
+            {
+              layers = xzalloc (sizeof *layers);
+              layers->style = area_style_override (
+                NULL, &pt->areas[PIVOT_AREA_LAYERS], NULL, NULL);
+              layers->layers = xnmalloc (layer_axis->n_dimensions,
+                                         sizeof *layers->layers);
+            }
 
-      ds_destroy (&layer_label);
+          const struct pivot_value *name
+            = d->data_leaves[layer_indexes[i]]->name;
+          struct table_item_layer *layer = &layers->layers[layers->n_layers++];
+          struct string s = DS_EMPTY_INITIALIZER;
+          pivot_value_format_body (name, pt->show_values, pt->show_variables,
+                                   &s);
+          layer->content = ds_steal_cstr (&s);
+          layer->n_footnotes = name->n_footnotes;
+          layer->footnotes = xnmalloc (layer->n_footnotes,
+                                       sizeof *layer->footnotes);
+          for (size_t i = 0; i < name->n_footnotes; i++)
+            layer->footnotes[i] = footnotes[name->footnotes[i]->idx];
+        }
+    }
+  if (layers)
+    {
+      table_item_set_layers (ti, layers);
+      table_item_layers_destroy (layers);
     }
 
   if (pt->caption)
index 636568f4eedd6bcf0f53b78b6b8cb8932e75fe33..83641c20622c56b798124142b47312c6ee301914 100644 (file)
@@ -1547,6 +1547,26 @@ add_text_page (struct render_pager *p, const struct table_item_text *t,
   render_pager_add_table (p, &tab->table, min_width);
 }
 
+static void
+add_layers_page (struct render_pager *p,
+                 const struct table_item_layers *layers, int min_width)
+{
+  if (!layers)
+    return;
+
+  struct tab_table *tab = tab_create (1, layers->n_layers);
+  for (size_t i = 0; i < layers->n_layers; i++)
+    {
+      const struct table_item_layer *layer = &layers->layers[i];
+      tab_text (tab, 0, i, 0, layer->content);
+      for (size_t j = 0; j < layer->n_footnotes; j++)
+        tab_add_footnote (tab, 0, i, layer->footnotes[j]);
+    }
+  if (layers->style)
+    tab->styles[0] = area_style_clone (tab->container, layers->style);
+  render_pager_add_table (p, &tab->table, min_width);
+}
+
 /* Creates and returns a new render_pager for rendering TABLE_ITEM on the
    device with the given PARAMS. */
 struct render_pager *
@@ -1570,7 +1590,7 @@ render_pager_create (const struct render_params *params,
   add_text_page (p, table_item_get_title (table_item), title_width);
 
   /* Layers. */
-  add_text_page (p, table_item_get_layers (table_item), title_width);
+  add_layers_page (p, table_item_get_layers (table_item), title_width);
 
   /* Body. */
   render_pager_add_table (p, table_ref (table_item_get_table (table_item)), 0);
index e9b854040a823982c36ac96690f7594c9a3a90b0..6243f03109429b446e34e90d83960e7bd95dd968 100644 (file)
@@ -24,6 +24,7 @@
 #include "libpspp/cast.h"
 #include "output/driver.h"
 #include "output/output-item-provider.h"
+#include "output/pivot-table.h"
 #include "output/table-item.h"
 
 #include "gl/xalloc.h"
@@ -68,6 +69,56 @@ table_item_text_destroy (struct table_item_text *text)
     }
 }
 
+void
+table_item_layer_copy (struct table_item_layer *dst,
+                       const struct table_item_layer *src)
+{
+  dst->content = xstrdup (src->content);
+  dst->footnotes = xmemdup (src->footnotes,
+                            src->n_footnotes * sizeof *src->footnotes);
+  dst->n_footnotes = src->n_footnotes;
+}
+
+void
+table_item_layer_uninit (struct table_item_layer *layer)
+{
+  if (layer)
+    {
+      free (layer->content);
+      free (layer->footnotes);
+    }
+}
+
+struct table_item_layers *
+table_item_layers_clone (const struct table_item_layers *old)
+{
+  if (!old)
+    return NULL;
+
+  struct table_item_layers *new = xmalloc (sizeof *new);
+  *new = (struct table_item_layers) {
+    .layers = xnmalloc (old->n_layers, sizeof *new->layers),
+    .n_layers = old->n_layers,
+    .style = area_style_clone (NULL, old->style),
+  };
+  for (size_t i = 0; i < new->n_layers; i++)
+    table_item_layer_copy (&new->layers[i], &old->layers[i]);
+  return new;
+}
+
+void
+table_item_layers_destroy (struct table_item_layers *layers)
+{
+  if (layers)
+    {
+      for (size_t i = 0; i < layers->n_layers; i++)
+        table_item_layer_uninit (&layers->layers[i]);
+      free (layers->layers);
+      area_style_free (layers->style);
+      free (layers);
+    }
+}
+
 /* Initializes ITEM as a table item for rendering TABLE.  The new table item
    initially has the specified TITLE and CAPTION, which may each be NULL.  The
    caller retains ownership of TITLE and CAPTION. */
@@ -114,7 +165,7 @@ table_item_set_title (struct table_item *item,
 
 /* Returns ITEM's layers, which will be a null pointer if no layers have been
    set. */
-const struct table_item_text *
+const struct table_item_layers *
 table_item_get_layers (const struct table_item *item)
 {
   return item->layers;
@@ -127,11 +178,11 @@ table_item_get_layers (const struct table_item *item)
    This function may only be used on a table_item that is unshared. */
 void
 table_item_set_layers (struct table_item *item,
-                      const struct table_item_text *layers)
+                       const struct table_item_layers *layers)
 {
   assert (!table_item_is_shared (item));
-  table_item_text_destroy (item->layers);
-  item->layers = table_item_text_clone (layers);
+  table_item_layers_destroy (item->layers);
+  item->layers = table_item_layers_clone (layers);
 }
 
 /* Returns ITEM's caption, which is a null pointer if no caption has been
@@ -169,8 +220,8 @@ table_item_destroy (struct output_item *output_item)
 {
   struct table_item *item = to_table_item (output_item);
   table_item_text_destroy (item->title);
-  table_item_text_destroy (item->layers);
   table_item_text_destroy (item->caption);
+  table_item_layers_destroy (item->layers);
   table_unref (item->table);
   free (item);
 }
index b7866a3c9b565f95fe996d62d562c604aab4d14e..24740f7eab625cedeecd2aa93416618c55705ae1 100644 (file)
@@ -42,6 +42,28 @@ struct table_item_text *table_item_text_create (const char *);
 struct table_item_text *table_item_text_clone (const struct table_item_text *);
 void table_item_text_destroy (struct table_item_text *);
 
+struct table_item_layer
+  {
+    char *content;
+    const struct footnote **footnotes;
+    size_t n_footnotes;
+  };
+
+void table_item_layer_copy (struct table_item_layer *,
+                            const struct table_item_layer *);
+void table_item_layer_uninit (struct table_item_layer *);
+
+struct table_item_layers
+  {
+    struct table_item_layer *layers;
+    size_t n_layers;
+    struct area_style *style;
+  };
+
+struct table_item_layers *table_item_layers_clone (
+  const struct table_item_layers *);
+void table_item_layers_destroy (struct table_item_layers *);
+
 /* A table item.
 
    The members of struct table_item should not be accessed directly.  Use one
@@ -51,8 +73,8 @@ struct table_item
     struct output_item output_item;  /* Superclass. */
     struct table *table;             /* The table to be rendered. */
     struct table_item_text *title;   /* Null if there is no title. */
-    struct table_item_text *layers;  /* Null if there is no layer info. */
     struct table_item_text *caption; /* Null if there is no caption. */
+    struct table_item_layers *layers; /* Null if there is no layer info. */
   };
 
 struct table_item *table_item_create (struct table *, const char *title,
@@ -64,10 +86,10 @@ const struct table_item_text *table_item_get_title (const struct table_item *);
 void table_item_set_title (struct table_item *,
                            const struct table_item_text *);
 
-const struct table_item_text *table_item_get_layers (
+const struct table_item_layers *table_item_get_layers (
   const struct table_item *);
 void table_item_set_layers (struct table_item *,
-                           const struct table_item_text *);
+                           const struct table_item_layers *);
 
 const struct table_item_text *table_item_get_caption (
   const struct table_item *);
index 86b7bc474fef918462bdb2a7ca982d64ff9a221a..7688d8c2fcd897f23f4718df6ba572415c26c688 100644 (file)
@@ -289,6 +289,15 @@ table_collect_footnotes (const struct table_item *item,
     footnotes = add_footnotes (title->footnotes, title->n_footnotes,
                                footnotes, &allocated, &n);
 
+  const struct table_item_layers *layers = table_item_get_layers (item);
+  if (layers)
+    {
+      for (size_t i = 0; i < layers->n_layers; i++)
+        footnotes = add_footnotes (layers->layers[i].footnotes,
+                                   layers->layers[i].n_footnotes,
+                                   footnotes, &allocated, &n);
+    }
+
   const struct table_item_text *caption = table_item_get_caption (item);
   if (caption)
     footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,