Add support for .tlo TableLook files from SPSS 15 and earlier.
[pspp] / src / output / table.c
index 1e3c53d256d29b5c0a9c9900ad6a70fc131f958d..c27d208f72b09b4fcf5aa435b3738035618d5388 100644 (file)
@@ -30,7 +30,7 @@
 #include "libpspp/pool.h"
 #include "libpspp/str.h"
 #include "output/table-item.h"
-#include "output/tab.h"
+#include "output/table.h"
 #include "output/text-item.h"
 
 #include "gl/xalloc.h"
@@ -67,10 +67,10 @@ table_is_shared (const struct table *table)
   return table->ref_cnt > 1;
 }
 \f
-struct area_style *
-area_style_clone (struct pool *pool, const struct area_style *old)
+struct table_area_style *
+table_area_style_clone (struct pool *pool, const struct table_area_style *old)
 {
-  struct area_style *new = pool_malloc (pool, sizeof *new);
+  struct table_area_style *new = pool_malloc (pool, sizeof *new);
   *new = *old;
   if (new->font_style.typeface)
     new->font_style.typeface = pool_strdup (pool, new->font_style.typeface);
@@ -78,7 +78,7 @@ area_style_clone (struct pool *pool, const struct area_style *old)
 }
 
 void
-area_style_free (struct area_style *style)
+table_area_style_free (struct table_area_style *style)
 {
   if (style)
     {
@@ -175,8 +175,14 @@ table_collect_footnotes (const struct table_item *item,
 struct table *
 table_from_string (const char *text)
 {
-  struct table *t = tab_create (1, 1, 0, 0, 0, 0);
-  tab_text (t, 0, 0, TAB_LEFT, text);
+  struct table *t = table_create (1, 1, 0, 0, 0, 0);
+  t->styles[0] = pool_alloc (t->container, sizeof *t->styles[0]);
+  *t->styles[0] = (struct table_area_style) {
+    TABLE_AREA_STYLE_INITIALIZER__,
+    .cell_style.halign = TABLE_HALIGN_LEFT,
+    .cell_style.valign = TABLE_VALIGN_TOP
+  };
+  table_text (t, 0, 0, 0 << TAB_STYLE_SHIFT, text);
   return t;
 }
 \f
@@ -228,11 +234,12 @@ table_halign_interpret (enum table_halign halign, bool numeric)
 }
 
 void
-font_style_copy (struct font_style *dst, const struct font_style *src)
+font_style_copy (struct pool *container,
+                 struct font_style *dst, const struct font_style *src)
 {
   *dst = *src;
   if (dst->typeface)
-    dst->typeface = xstrdup (dst->typeface);
+    dst->typeface = pool_strdup (container, dst->typeface);
 }
 
 void
@@ -243,14 +250,15 @@ font_style_uninit (struct font_style *font)
 }
 
 void
-area_style_copy (struct area_style *dst, const struct area_style *src)
+table_area_style_copy (struct pool *container, struct table_area_style *dst,
+                       const struct table_area_style *src)
 {
-  font_style_copy (&dst->font_style, &src->font_style);
+  font_style_copy (container, &dst->font_style, &src->font_style);
   dst->cell_style = src->cell_style;
 }
 
 void
-area_style_uninit (struct area_style *area)
+table_area_style_uninit (struct table_area_style *area)
 {
   if (area)
     font_style_uninit (&area->font_style);
@@ -319,18 +327,6 @@ cell_style_dump (const struct cell_style *c)
 
 static const bool debugging = true;
 
-/* Joined cell. */
-struct tab_joined_cell
-{
-  int d[TABLE_N_AXES][2];       /* Table region, same as struct table_cell. */
-  char *text;
-
-  size_t n_footnotes;
-  const struct footnote **footnotes;
-
-  const struct area_style *style;
-};
-
 /* Creates and returns a new table with NC columns and NR rows and initially no
    header rows or columns.
 
@@ -341,7 +337,7 @@ struct tab_joined_cell
 
    The table's cells are initially empty. */
 struct table *
-tab_create (int nc, int nr, int hl, int hr, int ht, int hb)
+table_create (int nc, int nr, int hl, int hr, int ht, int hb)
 {
   struct table *t;
 
@@ -358,10 +354,10 @@ tab_create (int nc, int nr, int hl, int hr, int ht, int hb)
   t->ct = pool_calloc (t->container, nr * nc, sizeof *t->ct);
 
   t->rh = pool_nmalloc (t->container, nc, nr + 1);
-  memset (t->rh, TAL_0, nc * (nr + 1));
+  memset (t->rh, TABLE_STROKE_NONE, nc * (nr + 1));
 
   t->rv = pool_nmalloc (t->container, nr, nc + 1);
-  memset (t->rv, TAL_0, nr * (nc + 1));
+  memset (t->rv, TABLE_STROKE_NONE, nr * (nc + 1));
 
   memset (t->styles, 0, sizeof t->styles);
   memset (t->rule_colors, 0, sizeof t->rule_colors);
@@ -374,7 +370,7 @@ tab_create (int nc, int nr, int hl, int hr, int ht, int hb)
 /* Draws a vertical line to the left of cells at horizontal position X
    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
 void
-tab_vline (struct table *t, int style, int x, int y1, int y2)
+table_vline (struct table *t, int style, int x, int y1, int y2)
 {
   if (debugging)
     {
@@ -405,7 +401,7 @@ tab_vline (struct table *t, int style, int x, int y1, int y2)
 /* Draws a horizontal line above cells at vertical position Y from X1
    to X2 inclusive in style STYLE, if style is not -1. */
 void
-tab_hline (struct table *t, int style, int x1, int x2, int y)
+table_hline (struct table *t, int style, int x1, int x2, int y)
 {
   if (debugging)
     {
@@ -440,8 +436,8 @@ tab_hline (struct table *t, int style, int x1, int x2, int y)
    drawing those lines.  This is distinct from 0, which draws a null
    line. */
 void
-tab_box (struct table *t, int f_h, int f_v, int i_h, int i_v,
-         int x1, int y1, int x2, int y2)
+table_box (struct table *t, int f_h, int f_v, int i_h, int i_v,
+           int x1, int y1, int x2, int y2)
 {
   if (debugging)
     {
@@ -511,7 +507,7 @@ tab_box (struct table *t, int f_h, int f_v, int i_h, int i_v,
 /* Cells. */
 
 static void
-do_tab_text (struct table *table, int c, int r, unsigned opt, char *text)
+do_table_text (struct table *table, int c, int r, unsigned opt, char *text)
 {
   assert (c >= 0);
   assert (r >= 0);
@@ -522,7 +518,7 @@ do_tab_text (struct table *table, int c, int r, unsigned opt, char *text)
     {
       if (c < 0 || r < 0 || c >= table_nc (table) || r >= table_nr (table))
         {
-          printf ("tab_text(): bad cell (%d,%d) in table size (%d,%d)\n",
+          printf ("table_text(): bad cell (%d,%d) in table size (%d,%d)\n",
                   c, r, table_nc (table), table_nr (table));
           return;
         }
@@ -535,32 +531,30 @@ do_tab_text (struct table *table, int c, int r, unsigned opt, char *text)
 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
    TEXT. */
 void
-tab_text (struct table *table, int c, int r, unsigned opt,
+table_text (struct table *table, int c, int r, unsigned opt,
           const char *text)
 {
-  do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
+  do_table_text (table, c, r, opt, pool_strdup (table->container, text));
 }
 
 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
    FORMAT, which is formatted as if passed to printf. */
 void
-tab_text_format (struct table *table, int c, int r, unsigned opt,
-                 const char *format, ...)
+table_text_format (struct table *table, int c, int r, unsigned opt,
+                   const char *format, ...)
 {
   va_list args;
 
   va_start (args, format);
-  do_tab_text (table, c, r, opt,
-               pool_vasprintf (table->container, format, args));
+  do_table_text (table, c, r, opt,
+                 pool_vasprintf (table->container, format, args));
   va_end (args);
 }
 
-static struct tab_joined_cell *
+static struct table_cell *
 add_joined_cell (struct table *table, int x1, int y1, int x2, int y2,
                  unsigned opt)
 {
-  struct tab_joined_cell *j;
-
   assert (x1 >= 0);
   assert (y1 >= 0);
   assert (y2 >= y1);
@@ -575,121 +569,146 @@ add_joined_cell (struct table *table, int x1, int y1, int x2, int y2,
           || x2 < x1 || x2 >= table_nc (table)
           || y2 < y1 || y2 >= table_nr (table))
         {
-          printf ("tab_joint_text(): bad cell "
+          printf ("table_joint_text(): bad cell "
                   "(%d,%d)-(%d,%d) in table size (%d,%d)\n",
                   x1, y1, x2, y2, table_nc (table), table_nr (table));
           return NULL;
         }
     }
 
-  tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
-
-  j = pool_alloc (table->container, sizeof *j);
-  j->d[TABLE_HORZ][0] = x1;
-  j->d[TABLE_VERT][0] = y1;
-  j->d[TABLE_HORZ][1] = ++x2;
-  j->d[TABLE_VERT][1] = ++y2;
-  j->n_footnotes = 0;
-  j->footnotes = NULL;
-  j->style = NULL;
-
-  {
-    void **cc = &table->cc[x1 + y1 * table_nc (table)];
-    unsigned short *ct = &table->ct[x1 + y1 * table_nc (table)];
-    const int ofs = table_nc (table) - (x2 - x1);
+  table_box (table, -1, -1, TABLE_STROKE_NONE, TABLE_STROKE_NONE,
+             x1, y1, x2, y2);
 
-    int y;
+  struct table_cell *cell = pool_alloc (table->container, sizeof *cell);
+  *cell = (struct table_cell) {
+    .d = { [TABLE_HORZ] = { x1, ++x2 },
+           [TABLE_VERT] = { y1, ++y2 } },
+    .options = opt,
+  };
 
-    for (y = y1; y < y2; y++)
-      {
-        int x;
-
-        for (x = x1; x < x2; x++)
-          {
-            *cc++ = j;
-            *ct++ = opt | TAB_JOIN;
-          }
+  void **cc = &table->cc[x1 + y1 * table_nc (table)];
+  unsigned short *ct = &table->ct[x1 + y1 * table_nc (table)];
+  const int ofs = table_nc (table) - (x2 - x1);
+  for (int y = y1; y < y2; y++)
+    {
+      for (int x = x1; x < x2; x++)
+        {
+          *cc++ = cell;
+          *ct++ = opt | TAB_JOIN;
+        }
 
-        cc += ofs;
-        ct += ofs;
-      }
-  }
+      cc += ofs;
+      ct += ofs;
+    }
 
-  return j;
+  return cell;
 }
 
 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
    options OPT to have text value TEXT. */
 void
-tab_joint_text (struct table *table, int x1, int y1, int x2, int y2,
-                unsigned opt, const char *text)
+table_joint_text (struct table *table, int x1, int y1, int x2, int y2,
+                  unsigned opt, const char *text)
 {
   char *s = pool_strdup (table->container, text);
   if (x1 == x2 && y1 == y2)
-    do_tab_text (table, x1, y1, opt, s);
+    do_table_text (table, x1, y1, opt, s);
   else
     add_joined_cell (table, x1, y1, x2, y2, opt)->text = s;
 }
 
-struct footnote *
-tab_create_footnote (struct table *table, size_t idx, const char *content,
-                     const char *marker, struct area_style *style)
-{
-  struct footnote *f = pool_alloc (table->container, sizeof *f);
-  f->idx = idx;
-  f->content = pool_strdup (table->container, content);
-  f->marker = pool_strdup (table->container, marker);
-  f->style = style;
-  return f;
-}
-
-void
-tab_add_footnote (struct table *table, int x, int y,
-                  const struct footnote *f)
+static struct table_cell *
+get_joined_cell (struct table *table, int x, int y)
 {
   int index = x + y * table_nc (table);
   unsigned short opt = table->ct[index];
-  struct tab_joined_cell *j;
+  struct table_cell *cell;
 
   if (opt & TAB_JOIN)
-    j = table->cc[index];
+    cell = table->cc[index];
   else
     {
       char *text = table->cc[index];
 
-      j = add_joined_cell (table, x, y, x, y, table->ct[index]);
-      j->text = text ? text : xstrdup ("");
+      cell = add_joined_cell (table, x, y, x, y, table->ct[index]);
+      cell->text = text ? text : pool_strdup (table->container, "");
     }
+  return cell;
+}
 
-  j->footnotes = pool_realloc (table->container, j->footnotes,
-                               (j->n_footnotes + 1) * sizeof *j->footnotes);
+/* Sets the subscripts for column X, row Y in TABLE. */
+void
+table_add_subscripts (struct table *table, int x, int y,
+                      char **subscripts, size_t n_subscripts)
+{
+  struct table_cell *cell = get_joined_cell (table, x, y);
 
-  j->footnotes[j->n_footnotes++] = f;
+  cell->n_subscripts = n_subscripts;
+  cell->subscripts = pool_nalloc (table->container, n_subscripts,
+                                  sizeof *cell->subscripts);
+  for (size_t i = 0; i < n_subscripts; i++)
+    cell->subscripts[i] = pool_strdup (table->container, subscripts[i]);
 }
 
+/* Sets the superscript for column X, row Y in TABLE. */
 void
-tab_add_style (struct table *table, int x, int y,
-               const struct area_style *style)
+table_add_superscript (struct table *table, int x, int y,
+                       const char *superscript)
 {
-  int index = x + y * table_nc (table);
-  unsigned short opt = table->ct[index];
-  struct tab_joined_cell *j;
+  get_joined_cell (table, x, y)->superscript
+    = pool_strdup (table->container, superscript);
+}
 
-  if (opt & TAB_JOIN)
-    j = table->cc[index];
-  else
-    {
-      char *text = table->cc[index];
+/* Create a footnote in TABLE with MARKER (e.g. "a") as its marker and CONTENT
+   as its content.  The footnote will be styled as STYLE, which is mandatory.
+   IDX must uniquely identify the footnote within TABLE.
 
-      j = add_joined_cell (table, x, y, x, y, table->ct[index]);
-      j->text = text ? text : xstrdup ("");
-    }
+   Returns the new footnote.  The return value is the only way to get to the
+   footnote later, so it is important for the caller to remember it. */
+struct footnote *
+table_create_footnote (struct table *table, size_t idx, const char *content,
+                       const char *marker, struct table_area_style *style)
+{
+  assert (style);
 
-  j->style = style;
+  struct footnote *f = pool_alloc (table->container, sizeof *f);
+  f->idx = idx;
+  f->content = pool_strdup (table->container, content);
+  f->marker = pool_strdup (table->container, marker);
+  f->style = style;
+  return f;
 }
 
+/* Attaches a reference to footnote F to the cell at column X, row Y in
+   TABLE. */
+void
+table_add_footnote (struct table *table, int x, int y,
+                    const struct footnote *f)
+{
+  assert (f->style);
+
+  struct table_cell *cell = get_joined_cell (table, x, y);
+
+  cell->footnotes = pool_realloc (
+    table->container, cell->footnotes,
+    (cell->n_footnotes + 1) * sizeof *cell->footnotes);
+
+  cell->footnotes[cell->n_footnotes++] = f;
+}
+
+/* Overrides the style for column X, row Y in TABLE with STYLE.
+   Does not make a copy of STYLE, so it should either be allocated from
+   TABLE->container or have a lifetime that will outlive TABLE. */
+void
+table_add_style (struct table *table, int x, int y,
+                 const struct table_area_style *style)
+{
+  get_joined_cell (table, x, y)->style = style;
+}
+
+/* Returns true if column C, row R has no contents, otherwise false. */
 bool
-tab_cell_is_empty (const struct table *table, int c, int r)
+table_cell_is_empty (const struct table *table, int c, int r)
 {
   return table->cc[c + r * table_nc (table)] == NULL;
 }
@@ -702,15 +721,15 @@ tab_cell_is_empty (const struct 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 UNUSED, const char *string)
+table_output_text (int options UNUSED, const char *string)
 {
   text_item_submit (text_item_create (TEXT_ITEM_LOG, string));
 }
 
-/* Same as tab_output_text(), but FORMAT is passed through printf-like
+/* Same as table_output_text(), but FORMAT is passed through printf-like
    formatting before output. */
 void
-tab_output_text_format (int options, const char *format, ...)
+table_output_text_format (int options, const char *format, ...)
 {
   va_list args;
   char *text;
@@ -719,7 +738,7 @@ tab_output_text_format (int options, const char *format, ...)
   text = xvasprintf (format, args);
   va_end (args);
 
-  tab_output_text (options, text);
+  table_output_text (options, text);
 
   free (text);
 }
@@ -739,66 +758,25 @@ table_get_cell (const struct table *t, int x, int y, struct table_cell *cell)
   unsigned short opt = t->ct[index];
   const void *cc = t->cc[index];
 
-  cell->options = opt;
-  cell->n_footnotes = 0;
-
-  int style_idx = (opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT;
-  const struct area_style *style = t->styles[style_idx];
-  if (style)
-    cell->style = style;
-  else
-    {
-      static const struct area_style styles[3][3] = {
-#define S(H,V) [H][V] = { AREA_STYLE_INITIALIZER__,     \
-                          .cell_style.halign = H,       \
-                          .cell_style.valign = V }
-        S(TABLE_HALIGN_LEFT, TABLE_VALIGN_TOP),
-        S(TABLE_HALIGN_LEFT, TABLE_VALIGN_CENTER),
-        S(TABLE_HALIGN_LEFT, TABLE_VALIGN_BOTTOM),
-        S(TABLE_HALIGN_CENTER, TABLE_VALIGN_TOP),
-        S(TABLE_HALIGN_CENTER, TABLE_VALIGN_CENTER),
-        S(TABLE_HALIGN_CENTER, TABLE_VALIGN_BOTTOM),
-        S(TABLE_HALIGN_RIGHT, TABLE_VALIGN_TOP),
-        S(TABLE_HALIGN_RIGHT, TABLE_VALIGN_CENTER),
-        S(TABLE_HALIGN_RIGHT, TABLE_VALIGN_BOTTOM),
-      };
-
-      enum table_halign halign
-        = ((opt & TAB_HALIGN) == TAB_LEFT ? TABLE_HALIGN_LEFT
-           : (opt & TAB_HALIGN) == TAB_CENTER ? TABLE_HALIGN_CENTER
-           : TABLE_HALIGN_RIGHT);
-      enum table_valign valign
-        = ((opt & TAB_VALIGN) == TAB_TOP ? TABLE_VALIGN_TOP
-           : (opt & TAB_VALIGN) == TAB_MIDDLE ? TABLE_VALIGN_CENTER
-           : TABLE_VALIGN_BOTTOM);
-
-      cell->style = &styles[halign][valign];
-    }
-
+  const struct table_area_style *style
+    = t->styles[(opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT];
   if (opt & TAB_JOIN)
     {
-      const struct tab_joined_cell *jc = cc;
-      cell->text = jc->text;
-
-      cell->footnotes = jc->footnotes;
-      cell->n_footnotes = jc->n_footnotes;
-
-      cell->d[TABLE_HORZ][0] = jc->d[TABLE_HORZ][0];
-      cell->d[TABLE_HORZ][1] = jc->d[TABLE_HORZ][1];
-      cell->d[TABLE_VERT][0] = jc->d[TABLE_VERT][0];
-      cell->d[TABLE_VERT][1] = jc->d[TABLE_VERT][1];
-
-      if (jc->style)
-        cell->style = jc->style;
+      const struct table_cell *jc = cc;
+      *cell = *jc;
+      if (!cell->style)
+        cell->style = style;
     }
   else
-    {
-      cell->d[TABLE_HORZ][0] = x;
-      cell->d[TABLE_HORZ][1] = x + 1;
-      cell->d[TABLE_VERT][0] = y;
-      cell->d[TABLE_VERT][1] = y + 1;
-      cell->text = CONST_CAST (char *, cc ? cc : "");
-    }
+    *cell = (struct table_cell) {
+      .d = { [TABLE_HORZ] = { x, x + 1 },
+             [TABLE_VERT] = { y, y + 1 } },
+      .options = opt,
+      .text = CONST_CAST (char *, cc ? cc : ""),
+      .style = style,
+    };
+
+  assert (cell->style);
 }
 
 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)