Add OUTPUT MODIFY command
[pspp] / src / output / render.c
index 05db149ab07a7a14b7a9b56f50ff8de0692d3ec5..a91e1f7b799570aa4a29689b984fdf17af33758c 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2009 Free Software Foundation, Inc.
+   Copyright (C) 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -359,9 +359,14 @@ distribute_spanned_width (int width,
      w1 by the common denominator of all three calculations (d), dividing that
      out in the column width calculation, and then keeping the remainder for
      the next iteration.
      w1 by the common denominator of all three calculations (d), dividing that
      out in the column width calculation, and then keeping the remainder for
      the next iteration.
+
+     (We actually compute the unspanned width of a column as twice the
+     unspanned width, plus the width of the rule on the left, plus the width of
+     the rule on the right.  That way each rule contributes to both the cell on
+     its left and on its right.)
   */
   d0 = n;
   */
   d0 = n;
-  d1 = total_unspanned * 2.0;
+  d1 = 2.0 * (total_unspanned > 0 ? total_unspanned : 1.0);
   d = d0 * d1;
   if (total_unspanned > 0)
     d *= 2.0;
   d = d0 * d1;
   if (total_unspanned > 0)
     d *= 2.0;
@@ -379,7 +384,7 @@ distribute_spanned_width (int width,
           w += width * unspanned * d0;
         }
 
           w += width * unspanned * d0;
         }
 
-      rows[x].width = w / d;
+      rows[x].width = MAX (rows[x].width, w / d);
       w -= rows[x].width * d;
     }
 }
       w -= rows[x].width * d;
     }
 }
@@ -450,7 +455,7 @@ measure_rule (const struct render_params *params, const struct table *table,
   enum table_axis b = !a;
   unsigned int rules;
   int d[TABLE_N_AXES];
   enum table_axis b = !a;
   unsigned int rules;
   int d[TABLE_N_AXES];
-  int width, i;
+  int width;
 
   /* Determine all types of rules that are present, as a bitmap in 'rules'
      where rule type 't' is present if bit 2**t is set. */
 
   /* Determine all types of rules that are present, as a bitmap in 'rules'
      where rule type 't' is present if bit 2**t is set. */
@@ -461,10 +466,11 @@ measure_rule (const struct render_params *params, const struct table *table,
 
   /* Calculate maximum width of the rules that are present. */
   width = 0;
 
   /* Calculate maximum width of the rules that are present. */
   width = 0;
-  for (i = 0; i < N_LINES; i++)
-    if (rules & (1u << i))
-      width = MAX (width, params->line_widths[a][rule_to_render_type (i)]);
-
+  if (rules & (1u << TAL_1)
+      || (z > 0 && z < table->n[a] && rules & (1u << TAL_GAP)))
+    width = params->line_widths[a][RENDER_LINE_SINGLE];
+  if (rules & (1u << TAL_2))
+    width = MAX (width, params->line_widths[a][RENDER_LINE_DOUBLE]);
   return width;
 }
 
   return width;
 }
 
@@ -778,6 +784,7 @@ render_page_unref (struct render_page *page)
 {
   if (page != NULL && --page->ref_cnt == 0)
     {
 {
   if (page != NULL && --page->ref_cnt == 0)
     {
+      int i;
       struct render_overflow *overflow, *next;
 
       HMAP_FOR_EACH_SAFE (overflow, next, struct render_overflow, node,
       struct render_overflow *overflow, *next;
 
       HMAP_FOR_EACH_SAFE (overflow, next, struct render_overflow, node,
@@ -786,8 +793,13 @@ render_page_unref (struct render_page *page)
       hmap_destroy (&page->overflows);
 
       table_unref (page->table);
       hmap_destroy (&page->overflows);
 
       table_unref (page->table);
-      free (page->cp[H]);
-      free (page->cp[V]);
+      
+      for (i = 0; i < TABLE_N_AXES; ++i)
+       {
+         free (page->join_crossing[i]);
+         free (page->cp[i]);
+       }
+
       free (page);
     }
 }
       free (page);
     }
 }
@@ -803,7 +815,7 @@ render_page_get_size (const struct render_page *page, enum table_axis axis)
 \f
 /* Drawing render_pages. */
 
 \f
 /* Drawing render_pages. */
 
-static enum render_line_style
+static inline enum render_line_style
 get_rule (const struct render_page *page, enum table_axis axis,
           const int d[TABLE_N_AXES])
 {
 get_rule (const struct render_page *page, enum table_axis axis,
           const int d[TABLE_N_AXES])
 {
@@ -902,15 +914,15 @@ render_cell (const struct render_page *page, const struct table_cell *cell)
   page->params->draw_cell (page->params->aux, cell, bb, clip);
 }
 
   page->params->draw_cell (page->params->aux, cell, bb, clip);
 }
 
-/* Renders PAGE, by calling the 'draw_line' and 'draw_cell' functions from the
-   render_params provided to render_page_create(). */
-void
-render_page_draw (const struct render_page *page)
+/* Draws the cells of PAGE indicated in BB. */
+static void
+render_page_draw_cells (const struct render_page *page,
+                        int bb[TABLE_N_AXES][2])
 {
   int x, y;
 
 {
   int x, y;
 
-  for (y = 0; y <= page->n[V] * 2; y++)
-    for (x = 0; x <= page->n[H] * 2; )
+  for (y = bb[V][0]; y < bb[V][1]; y++)
+    for (x = bb[H][0]; x < bb[H][1]; )
       if (is_rule (x) || is_rule (y))
         {
           int d[TABLE_N_AXES];
       if (is_rule (x) || is_rule (y))
         {
           int d[TABLE_N_AXES];
@@ -924,12 +936,91 @@ render_page_draw (const struct render_page *page)
           struct table_cell cell;
 
           table_get_cell (page->table, x / 2, y / 2, &cell);
           struct table_cell cell;
 
           table_get_cell (page->table, x / 2, y / 2, &cell);
-          if (y / 2 == cell.d[V][0])
+          if (y == bb[V][0] || y / 2 == cell.d[V][0])
             render_cell (page, &cell);
           x = rule_ofs (cell.d[H][1]);
           table_cell_free (&cell);
         }
 }
             render_cell (page, &cell);
           x = rule_ofs (cell.d[H][1]);
           table_cell_free (&cell);
         }
 }
+
+/* Renders PAGE, by calling the 'draw_line' and 'draw_cell' functions from the
+   render_params provided to render_page_create(). */
+void
+render_page_draw (const struct render_page *page)
+{
+  int bb[TABLE_N_AXES][2];
+
+  bb[H][0] = 0;
+  bb[H][1] = page->n[H] * 2 + 1;
+  bb[V][0] = 0;
+  bb[V][1] = page->n[V] * 2 + 1;
+
+  render_page_draw_cells (page, bb);
+}
+
+/* Returns the greatest value i, 0 <= i < n, such that cp[i] <= x0. */
+static int
+get_clip_min_extent (int x0, const int cp[], int n)
+{
+  int low, high, best;
+
+  low = 0;
+  high = n;
+  best = 0;
+  while (low < high)
+    {
+      int middle = low + (high - low) / 2;
+
+      if (cp[middle] <= x0)
+        {
+          best = middle;
+          low = middle + 1;
+        }
+      else
+        high = middle;
+    }
+
+  return best;
+}
+
+/* Returns the least value i, 0 <= i < n, such that cp[i + 1] >= x1. */
+static int
+get_clip_max_extent (int x1, const int cp[], int n)
+{
+  int low, high, best;
+
+  low = 0;
+  high = n;
+  best = n;
+  while (low < high)
+    {
+      int middle = low + (high - low) / 2;
+
+      if (cp[middle] >= x1)
+        best = high = middle;
+      else
+        low = middle + 1;
+    }
+
+  return best;
+}
+
+/* Renders the cells of PAGE that intersect (X,Y)-(X+W,Y+H), by calling the
+   'draw_line' and 'draw_cell' functions from the render_params provided to
+   render_page_create(). */
+void
+render_page_draw_region (const struct render_page *page,
+                         int x, int y, int w, int h)
+{
+  int bb[TABLE_N_AXES][2];
+
+  bb[H][0] = get_clip_min_extent (x, page->cp[H], page->n[H] * 2 + 1);
+  bb[H][1] = get_clip_max_extent (x + w, page->cp[H], page->n[H] * 2 + 1);
+  bb[V][0] = get_clip_min_extent (y, page->cp[V], page->n[V] * 2 + 1);
+  bb[V][1] = get_clip_max_extent (y + h, page->cp[V], page->n[V] * 2 + 1);
+
+  render_page_draw_cells (page, bb);
+}
 \f
 /* Breaking up tables to fit on a page. */
 
 \f
 /* Breaking up tables to fit on a page. */
 
@@ -955,12 +1046,27 @@ render_break_init (struct render_break *b, struct render_page *page,
   b->hw = headers_width (page, axis);
 }
 
   b->hw = headers_width (page, axis);
 }
 
+/* Initializes B as a render_break structure for which
+   render_break_has_next() always returns false. */
+void
+render_break_init_empty (struct render_break *b)
+{
+  b->page = NULL;
+  b->axis = TABLE_HORZ;
+  b->cell = 0;
+  b->pixel = 0;
+  b->hw = 0;
+}
+
 /* Frees B and unrefs the render_page that it owns. */
 void
 render_break_destroy (struct render_break *b)
 {
   if (b != NULL)
 /* Frees B and unrefs the render_page that it owns. */
 void
 render_break_destroy (struct render_break *b)
 {
   if (b != NULL)
-    render_page_unref (b->page);
+    {
+      render_page_unref (b->page);
+      b->page = NULL;
+    }
 }
 
 /* Returns true if B still has cells that are yet to be returned,
 }
 
 /* Returns true if B still has cells that are yet to be returned,
@@ -971,7 +1077,7 @@ render_break_has_next (const struct render_break *b)
   const struct render_page *page = b->page;
   enum table_axis axis = b->axis;
 
   const struct render_page *page = b->page;
   enum table_axis axis = b->axis;
 
-  return b->cell < page->n[axis] - page->h[axis][1];
+  return page != NULL && b->cell < page->n[axis] - page->h[axis][1];
 }
 
 /* Returns the minimum SIZE argument that, if passed to render_break_next(),
 }
 
 /* Returns the minimum SIZE argument that, if passed to render_break_next(),
@@ -1112,7 +1218,7 @@ render_page_select (const struct render_page *page, enum table_axis axis,
   if (z0 == page->h[a][0] && p0 == 0
       && z1 == page->n[a] - page->h[a][1] && p1 == 0)
     {
   if (z0 == page->h[a][0] && p0 == 0
       && z1 == page->n[a] - page->h[a][1] && p1 == 0)
     {
-      struct render_page *page_rw = (struct render_page *) page;
+      struct render_page *page_rw = CONST_CAST (struct render_page *, page);
       page_rw->ref_cnt++;
       return page_rw;
     }
       page_rw->ref_cnt++;
       return page_rw;
     }
@@ -1299,4 +1405,3 @@ insert_overflow (struct render_page_selection *s,
 
   return of;
 }
 
   return of;
 }
-