output: Remove table_unshare.
[pspp] / src / output / table.c
index e354bb3edfc540a9b920a61e68a3e59fe773d411..3690df3bd6c48593ee6832fabdf6a3723cb17a26 100644 (file)
 #include "output/table-provider.h"
 
 #include <assert.h>
+#include <inttypes.h>
 #include <stdlib.h>
 
+#include "data/format.h"
+#include "libpspp/assertion.h"
 #include "libpspp/cast.h"
 #include "libpspp/compiler.h"
+#include "libpspp/pool.h"
+#include "libpspp/str.h"
 #include "output/table-item.h"
 
 #include "gl/xalloc.h"
@@ -128,6 +133,26 @@ table_set_nr (struct table *table, int nr)
   table->n[TABLE_VERT] = nr;
 }
 \f
+struct area_style *
+area_style_clone (struct pool *pool, const struct area_style *old)
+{
+  struct 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);
+  return new;
+}
+
+void
+area_style_free (struct area_style *style)
+{
+  if (style)
+    {
+      free (style->font_style.typeface);
+      free (style);
+    }
+}
+
 /* Initializes CELL with the contents of the table cell at column X and row Y
    within TABLE.  When CELL is no longer needed, the caller is responsible for
    freeing it by calling table_cell_free(CELL).
@@ -139,6 +164,10 @@ table_get_cell (const struct table *table, int x, int y,
 {
   assert (x >= 0 && x < table->n[TABLE_HORZ]);
   assert (y >= 0 && y < table->n[TABLE_VERT]);
+
+  static const struct area_style default_style = AREA_STYLE_INITIALIZER;
+  cell->style = &default_style;
+
   table->klass->get_cell (table, x, y, cell);
 }
 
@@ -189,93 +218,90 @@ table_cell_free (struct table_cell *cell)
    between that cell and cell (0,1); and so on, up to (0,NR), which runs
    horizontally below cell (0,NR-1). */
 int
-table_get_rule (const struct table *table, enum table_axis axis, int x, int y)
+table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
+                struct cell_color *color)
 {
   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
-  return table->klass->get_rule (table, axis, x, y);
+  *color = (struct cell_color) CELL_COLOR_BLACK;
+  return table->klass->get_rule (table, axis, x, y, color);
 }
-\f
-struct table_unshared
-  {
-    struct table table;
-    struct table *subtable;
-  };
-
-static const struct table_class table_unshared_class;
-
-/* Takes ownership of TABLE and returns a table with the same contents but
-   which is guaranteed not to be shared (as returned by table_is_shared()).
 
-   If TABLE is unshared, just returns TABLE.
-
-   The only real use for this function is to create a copy of TABLE in which
-   the headers can be adjusted, which is a pretty specialized use case. */
-struct table *
-table_unshare (struct table *table)
+void
+table_cell_format_footnote_markers (const struct table_cell *cell,
+                                    struct string *s)
 {
-  if (!table_is_shared (table))
-    return table;
-  else
+  for (size_t i = 0; i < cell->n_footnotes; i++)
     {
-      struct table_unshared *tiu = xmalloc (sizeof *tiu);
-      table_init (&tiu->table, &table_unshared_class);
-      table_set_nc (&tiu->table, table_nc (table));
-      table_set_nr (&tiu->table, table_nr (table));
-      table_set_hl (&tiu->table, table_hl (table));
-      table_set_hr (&tiu->table, table_hr (table));
-      table_set_ht (&tiu->table, table_ht (table));
-      table_set_hb (&tiu->table, table_hb (table));
-      tiu->subtable = table;
-      return &tiu->table;
+      if (i)
+        ds_put_byte (s, ',');
+      ds_put_cstr (s, cell->footnotes[i]->marker);
     }
 }
 
-static struct table_unshared *
-table_unshared_cast (const struct table *table)
+static const struct footnote **
+add_footnotes (const struct footnote **refs, size_t n_refs,
+               const struct footnote **footnotes, size_t *allocated, size_t *n)
 {
-  assert (table->klass == &table_unshared_class);
-  return UP_CAST (table, struct table_unshared, table);
+  for (size_t i = 0; i < n_refs; i++)
+    {
+      const struct footnote *f = refs[i];
+      if (f->idx >= *allocated)
+        {
+          size_t new_allocated = (f->idx + 1) * 2;
+          footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
+          while (*allocated < new_allocated)
+            footnotes[(*allocated)++] = NULL;
+        }
+      footnotes[f->idx] = f;
+      if (f->idx >= *n)
+        *n = f->idx + 1;
+    }
+  return footnotes;
 }
 
-static void
-table_unshared_destroy (struct table *tiu_)
+size_t
+table_collect_footnotes (const struct table_item *item,
+                         const struct footnote ***footnotesp)
 {
-  struct table_unshared *tiu = table_unshared_cast (tiu_);
-  table_unref (tiu->subtable);
-  free (tiu);
-}
+  const struct footnote **footnotes = NULL;
+  size_t allocated = 0;
+  size_t n = 0;
 
-static void
-table_unshared_get_cell (const struct table *tiu_, int x, int y,
-                              struct table_cell *cell)
-{
-  struct table_unshared *tiu = table_unshared_cast (tiu_);
-  table_get_cell (tiu->subtable, x, y, cell);
-}
+  struct table *t = item->table;
+  for (int y = 0; y < table_nr (t); y++)
+    {
+      struct table_cell cell;
+      for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
+        {
+          table_get_cell (t, x, y, &cell);
+
+          if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
+            footnotes = add_footnotes (cell.footnotes, cell.n_footnotes,
+                                       footnotes, &allocated, &n);
+          table_cell_free (&cell);
+        }
+    }
 
-static int
-table_unshared_get_rule (const struct table *tiu_,
-                              enum table_axis axis, int x, int y)
-{
-  struct table_unshared *tiu = table_unshared_cast (tiu_);
-  return table_get_rule (tiu->subtable, axis, x, y);
-}
+  const struct table_item_text *title = table_item_get_title (item);
+  if (title)
+    footnotes = add_footnotes (title->footnotes, title->n_footnotes,
+                               footnotes, &allocated, &n);
 
-static const struct table_class table_unshared_class =
-  {
-    table_unshared_destroy,
-    table_unshared_get_cell,
-    table_unshared_get_rule,
-    NULL,                       /* paste */
-    NULL,                       /* select */
-  };
+  const struct table_item_text *caption = table_item_get_caption (item);
+  if (caption)
+    footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
+                               footnotes, &allocated, &n);
+
+  *footnotesp = footnotes;
+  return n;
+}
 \f
 struct table_string
   {
     struct table table;
     char *string;
-    unsigned int options;
+    enum table_halign halign;
   };
 
 static const struct table_class table_string_class;
@@ -283,13 +309,13 @@ static const struct table_class table_string_class;
 /* Returns a table that contains a single cell, whose contents are S with
    options OPTIONS (a combination of TAB_* values).  */
 struct table *
-table_from_string (unsigned int options, const char *s)
+table_from_string (enum table_halign halign, const char *s)
 {
   struct table_string *ts = xmalloc (sizeof *ts);
   table_init (&ts->table, &table_string_class);
   ts->table.n[TABLE_HORZ] = ts->table.n[TABLE_VERT] = 1;
   ts->string = xstrdup (s);
-  ts->options = options;
+  ts->halign = halign;
   return &ts->table;
 }
 
@@ -312,24 +338,31 @@ static void
 table_string_get_cell (const struct table *ts_, int x UNUSED, int y UNUSED,
                        struct table_cell *cell)
 {
+  static const struct area_style styles[] = {
+#define S(H) [H] = { AREA_STYLE_INITIALIZER__, .cell_style.halign = H }
+    S(TABLE_HALIGN_LEFT),
+    S(TABLE_HALIGN_CENTER),
+    S(TABLE_HALIGN_RIGHT),
+    S(TABLE_HALIGN_MIXED),
+    S(TABLE_HALIGN_DECIMAL),
+  };
   struct table_string *ts = table_string_cast (ts_);
   cell->d[TABLE_HORZ][0] = 0;
   cell->d[TABLE_HORZ][1] = 1;
   cell->d[TABLE_VERT][0] = 0;
   cell->d[TABLE_VERT][1] = 1;
-  cell->contents = &cell->inline_contents;
-  cell->inline_contents.options = ts->options;
-  cell->inline_contents.text = ts->string;
-  cell->inline_contents.table = NULL;
-  cell->inline_contents.n_footnotes = 0;
-  cell->n_contents = 1;
+  cell->options = 0;
+  cell->style = &styles[table_halign_interpret (ts->halign, false)];
+  cell->text = ts->string;
+  cell->n_footnotes = 0;
   cell->destructor = NULL;
 }
 
 
 static int
 table_string_get_rule (const struct table *ts UNUSED,
-                       enum table_axis axis UNUSED, int x UNUSED, int y UNUSED)
+                       enum table_axis axis UNUSED, int x UNUSED, int y UNUSED,
+                       struct cell_color *color UNUSED)
 {
   return TAL_0;
 }
@@ -339,84 +372,140 @@ static const struct table_class table_string_class =
     table_string_destroy,
     table_string_get_cell,
     table_string_get_rule,
-    NULL,                       /* paste */
-    NULL,                       /* select */
   };
 \f
-struct table_nested
-  {
-    struct table table;
-    struct table_item *inner;
-  };
+const char *
+table_halign_to_string (enum table_halign halign)
+{
+  switch (halign)
+    {
+    case TABLE_HALIGN_LEFT: return "left";
+    case TABLE_HALIGN_CENTER: return "center";
+    case TABLE_HALIGN_RIGHT: return "right";
+    case TABLE_HALIGN_DECIMAL: return "decimal";
+    case TABLE_HALIGN_MIXED: return "mixed";
+    default: return "**error**";
+    }
+}
 
-static const struct table_class table_nested_class;
+const char *
+table_valign_to_string (enum table_valign valign)
+{
+  switch (valign)
+    {
+    case TABLE_VALIGN_TOP: return "top";
+    case TABLE_VALIGN_CENTER: return "center";
+    case TABLE_VALIGN_BOTTOM: return "bottom";
+    default: return "**error**";
+    }
+}
 
-/* Creates and returns a table with a single cell that contains INNER.
-   Takes ownership of INNER. */
-struct table *
-table_create_nested (struct table *inner)
+enum table_halign
+table_halign_interpret (enum table_halign halign, bool numeric)
 {
-  return table_create_nested_item (table_item_create (inner, NULL, NULL));
+  switch (halign)
+    {
+    case TABLE_HALIGN_LEFT:
+    case TABLE_HALIGN_CENTER:
+    case TABLE_HALIGN_RIGHT:
+      return halign;
+
+    case TABLE_HALIGN_MIXED:
+      return numeric ? TABLE_HALIGN_RIGHT : TABLE_HALIGN_LEFT;
+
+    case TABLE_HALIGN_DECIMAL:
+      return TABLE_HALIGN_DECIMAL;
+
+    default:
+      NOT_REACHED ();
+    }
 }
 
-/* Creates and returns a table with a single cell that contains INNER.
-   Takes ownership of INNER. */
-struct table *
-table_create_nested_item (struct table_item *inner)
+void
+font_style_copy (struct font_style *dst, const struct font_style *src)
 {
-  struct table_nested *tn = xmalloc (sizeof *tn);
-  table_init (&tn->table, &table_nested_class);
-  tn->table.n[TABLE_HORZ] = tn->table.n[TABLE_VERT] = 1;
-  tn->inner = inner;
-  return &tn->table;
+  *dst = *src;
+  if (dst->typeface)
+    dst->typeface = xstrdup (dst->typeface);
 }
 
-static struct table_nested *
-table_nested_cast (const struct table *table)
+void
+font_style_uninit (struct font_style *font)
 {
-  assert (table->klass == &table_nested_class);
-  return UP_CAST (table, struct table_nested, table);
+  if (font)
+    free (font->typeface);
 }
 
-static void
-table_nested_destroy (struct table *tn_)
+void
+area_style_copy (struct area_style *dst, const struct area_style *src)
 {
-  struct table_nested *tn = table_nested_cast (tn_);
-  table_item_unref (tn->inner);
-  free (tn);
+  font_style_copy (&dst->font_style, &src->font_style);
+  dst->cell_style = src->cell_style;
 }
 
-static void
-table_nested_get_cell (const struct table *tn_, int x UNUSED, int y UNUSED,
-                       struct table_cell *cell)
+void
+area_style_uninit (struct area_style *area)
 {
-  struct table_nested *tn = table_nested_cast (tn_);
-  cell->d[TABLE_HORZ][0] = 0;
-  cell->d[TABLE_HORZ][1] = 1;
-  cell->d[TABLE_VERT][0] = 0;
-  cell->d[TABLE_VERT][1] = 1;
-  cell->contents = &cell->inline_contents;
-  cell->inline_contents.options = TAB_LEFT;
-  cell->inline_contents.text = NULL;
-  cell->inline_contents.table = tn->inner;
-  cell->inline_contents.n_footnotes = 0;
-  cell->n_contents = 1;
-  cell->destructor = NULL;
+  if (area)
+    font_style_uninit (&area->font_style);
 }
 
-static int
-table_nested_get_rule (const struct table *tn UNUSED,
-                       enum table_axis axis UNUSED, int x UNUSED, int y UNUSED)
+const char *
+table_stroke_to_string (enum table_stroke stroke)
 {
-  return TAL_0;
+  switch (stroke)
+    {
+    case TABLE_STROKE_NONE: return "none";
+    case TABLE_STROKE_SOLID: return "solid";
+    case TABLE_STROKE_DASHED: return "dashed";
+    case TABLE_STROKE_THICK: return "thick";
+    case TABLE_STROKE_THIN: return "thin";
+    case TABLE_STROKE_DOUBLE: return "double";
+    default:
+      return "**error**";
+    }
 }
 
-static const struct table_class table_nested_class =
-  {
-    table_nested_destroy,
-    table_nested_get_cell,
-    table_nested_get_rule,
-    NULL,                       /* paste */
-    NULL,                       /* select */
-  };
+void
+cell_color_dump (const struct cell_color *c)
+{
+  if (c->alpha != 255)
+    printf ("rgba(%d, %d, %d, %d)", c->r, c->g, c->b, c->alpha);
+  else
+    printf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8, c->r, c->g, c->b);
+}
+
+void
+font_style_dump (const struct font_style *f)
+{
+  printf ("%s %dpx ", f->typeface, f->size);
+  cell_color_dump (&f->fg[0]);
+  putchar ('/');
+  cell_color_dump (&f->bg[0]);
+  if (!cell_color_equal (&f->fg[0], &f->fg[1])
+      || !cell_color_equal (&f->bg[0], &f->bg[1]))
+    {
+      printf (" alt=");
+      cell_color_dump (&f->fg[1]);
+      putchar ('/');
+      cell_color_dump (&f->bg[1]);
+    }
+  if (f->bold)
+    fputs (" bold", stdout);
+  if (f->italic)
+    fputs (" italic", stdout);
+  if (f->underline)
+    fputs (" underline", stdout);
+}
 
+void
+cell_style_dump (const struct cell_style *c)
+{
+  fputs (table_halign_to_string (c->halign), stdout);
+  if (c->halign == TABLE_HALIGN_DECIMAL)
+    printf ("(%.2gpx)", c->decimal_offset);
+  printf (" %s", table_valign_to_string (c->valign));
+  printf (" %d,%d,%d,%dpx",
+          c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1],
+          c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]);
+}