LIST: Remove WEIGHT subcommand.
[pspp-builds.git] / src / output / manager.c
index ba9cfe94c4673898962a6918438cc60734e47b31..9b9013933ae87ed3fde10e6d93e83441a41a8ad2 100644 (file)
@@ -1,32 +1,56 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2007, 2009 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 the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   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
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
 
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA. */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
-#include "manager.h"
+
+#include <output/manager.h>
+
 #include <stdio.h>
 #include <stdlib.h>
-#include "output.h"
+
+#include <libpspp/assertion.h>
+#include <output/output.h>
+
+#include "gl/xalloc.h"
 
 /* Table. */
-int table_num = 1;
-int subtable_num;
+static int table_num = 1;
+static int subtable_num;
+
+/* Name of PSPP's current command, or NULL if outside a command. */
+static char *command_name;
 \f
+struct som_entity *
+som_entity_clone (struct som_entity *entity)
+{
+  struct som_entity *copy = xmemdup (entity, sizeof *entity);
+  copy->command_name = xstrdup (entity->command_name);
+  return copy;
+}
+
+void
+som_entity_destroy (struct som_entity *entity)
+{
+  if (entity != NULL)
+    {
+      free (entity->command_name);
+      free (entity);
+    }
+}
+
 /* Increments table_num so different procedures' output can be
    distinguished. */
 void
@@ -39,6 +63,15 @@ som_new_series (void)
     }
 }
 
+/* Sets COMMAND_NAME as the name of the current command,
+   for embedding in output. */
+void
+som_set_command_name (const char *command_name_)
+{
+  free (command_name);
+  command_name = command_name_ ? xstrdup (command_name_) : NULL;
+}
+
 /* Ejects the paper for all active devices. */
 void
 som_eject_page (void)
@@ -49,41 +82,37 @@ som_eject_page (void)
     outp_eject_page (d);
 }
 
+/* Flushes output on all active devices. */
+void
+som_flush (void)
+{
+  struct outp_driver *d;
+
+  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
+    outp_flush (d);
+}
+
 /* Skip down a single line on all active devices. */
 void
 som_blank_line (void)
 {
   struct outp_driver *d;
-  
+
   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
     if (d->page_open && d->cp_y != 0)
       d->cp_y += d->font_height;
 }
 \f
-/* Driver. */
-static struct outp_driver *d=0;
-
-/* Table. */
-static struct som_entity *t=0;
-
-/* Flags. */
-static unsigned flags;
-
-/* Number of columns, rows. */
-static int nc, nr;
-
-/* Number of columns or rows in left, right, top, bottom headers. */
-static int hl, hr, ht, hb;
-
-/* Column style. */
-static int cs;
-
-/* Table height, width. */
-static int th, tw;
-
-static void render_columns (void);
-static void render_simple (void);
-static void render_segments (void);
+static void render_columns (void *r, struct outp_driver *, struct som_entity *,
+                            int tw, int th,
+                            int hl, int hr, int ht, int hb);
+static void render_simple (void *r, struct outp_driver *, struct som_entity *,
+                           int tw, int th,
+                           int hl, int hr, int ht, int hb);
+static void render_segments (void *r, struct outp_driver *,
+                             struct som_entity *,
+                             int tw, int th,
+                             int hl, int hr, int ht, int hb);
 
 static void output_entity (struct outp_driver *, struct som_entity *);
 
@@ -91,116 +120,171 @@ static void output_entity (struct outp_driver *, struct som_entity *);
 void
 som_submit (struct som_entity *t)
 {
+  struct outp_driver *d;
+  unsigned int flags;
+
 #if DEBUGGING
   static int entry;
-  
+
   assert (entry++ == 0);
 #endif
 
-  if ( t->type == SOM_TABLE) 
-    {
-      t->class->table (t);
-      t->class->flags (&flags);
-      t->class->count (&nc, &nr);
-      t->class->headers (&hl, &hr, &ht, &hb);
+  t->class->flags (t, &flags);
+  if (!(flags & SOMF_NO_TITLE))
+    subtable_num++;
+  t->table_num = table_num;
+  t->subtable_num = subtable_num;
+  t->command_name = command_name ? xstrdup (command_name) : NULL;
 
+  if (t->type == SOM_TABLE)
+    {
+      int hl, hr, ht, hb;
+      int nc, nr;
 
-#if DEBUGGING
+      t->class->count (t, &nc, &nr);
+      t->class->headers (t, &hl, &hr, &ht, &hb);
       if (hl + hr > nc || ht + hb > nr)
        {
-         printf ("headers: (l,r)=(%d,%d), (t,b)=(%d,%d) in table size (%d,%d)\n",
-                 hl, hr, ht, hb, nc, nr);
-         abort ();
+         fprintf (stderr, "headers: (l,r)=(%d,%d), (t,b)=(%d,%d) "
+                   "in table size (%d,%d)\n",
+                   hl, hr, ht, hb, nc, nr);
+         NOT_REACHED ();
        }
       else if (hl + hr == nc)
-       printf ("warning: headers (l,r)=(%d,%d) in table width %d\n", hl, hr, nc);
+       fprintf (stderr, "warning: headers (l,r)=(%d,%d) in table width %d\n",
+                hl, hr, nc);
       else if (ht + hb == nr)
-       printf ("warning: headers (t,b)=(%d,%d) in table height %d\n", ht, hb, nr);
-#endif
+       fprintf (stderr, "warning: headers (t,b)=(%d,%d) in table height %d\n",
+                ht, hb, nr);
+    }
 
-      t->class->columns (&cs);
+  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
+    output_entity (d, t);
 
-      if (!(flags & SOMF_NO_TITLE))
-       subtable_num++;
-  
-    }
-  
-  {
-    struct outp_driver *d;
-    
-    for (d = outp_drivers (NULL); d; d = outp_drivers (d))
-       output_entity (d, t);
-
-  }
-  
 #if DEBUGGING
   assert (--entry == 0);
 #endif
 }
 
-/* Output entity ENTITY to driver DRIVER. */
+static bool
+check_fits_width (struct som_entity *t, const struct outp_driver *d, void *r)
+{
+  int hl, hr, ht, hb;
+  int nc, nr;
+  int i;
+
+  t->class->headers (t, &hl, &hr, &ht, &hb);
+  t->class->count (t, &nc, &nr);
+  for (i = hl; i < nc - hr; i++)
+    {
+      int end, actual;
+      t->class->cumulate (r, SOM_COLUMNS, i, &end, d->width, &actual);
+      if (end == i)
+        return false;
+    }
+
+  return true;
+}
+
+static bool
+check_fits_length (struct som_entity *t, const struct outp_driver *d, void *r)
+{
+  int hl, hr, ht, hb;
+  int nc, nr;
+  int i;
+
+  t->class->headers (t, &hl, &hr, &ht, &hb);
+  t->class->count (t, &nc, &nr);
+  for (i = ht; i < nr - hb; i++)
+    {
+      int end, actual;
+      t->class->cumulate (r, SOM_ROWS, i, &end, d->length, &actual);
+      if (end == i)
+        return false;
+    }
+
+  return true;
+}
+
+/* Output entity T to driver D. */
 static void
-output_entity (struct outp_driver *driver, struct som_entity *entity)
+output_entity (struct outp_driver *d, struct som_entity *t)
 {
   bool fits_width, fits_length;
-  d = driver;
+  unsigned int flags;
+  int hl, hr, ht, hb;
+  int tw, th;
+  int nc, nr;
+  int cs;
+  void *r;
 
   outp_open_page (d);
-  if (d->class->special || entity->type == SOM_CHART)
+  if (d->class->special)
     {
-      driver->class->submit (d, entity);
+      d->class->submit (d, t);
       return;
     }
 
-  t = entity;
-  
-  t->class->driver (d);
-  t->class->area (&tw, &th);
-  fits_width = t->class->fits_width (d->width);
-  fits_length = t->class->fits_length (d->length);
-  if (!fits_width || !fits_length) 
+  t->class->headers (t, &hl, &hr, &ht, &hb);
+  t->class->count (t, &nc, &nr);
+  t->class->columns (t, &cs);
+  t->class->flags (t, &flags);
+
+  r = t->class->render_init (t, d, hl, hr, ht, hb);
+
+  fits_width = check_fits_width (t, d, r);
+  fits_length = check_fits_length (t, d, r);
+  if (!fits_width || !fits_length)
     {
-      int tl, tr, tt, tb;
-      tl = fits_width ? hl : 0;
-      tr = fits_width ? hr : 0;
-      tt = fits_length ? ht : 0;
-      tb = fits_length ? hb : 0;
-      t->class->set_headers (tl, tr, tt, tb);
-      t->class->driver (d);
-      t->class->area (&tw, &th);
+      t->class->render_free (r);
+
+      if (!fits_width)
+        hl = hr = 0;
+      if (!fits_length)
+        ht = hb = 0;
+
+      r = t->class->render_init (t, d, hl, hr, ht, hb);
     }
-  
+  t->class->area (r, &tw, &th);
+
   if (!(flags & SOMF_NO_SPACING) && d->cp_y != 0)
     d->cp_y += d->font_height;
-       
+
   if (cs != SOM_COL_NONE
       && 2 * (tw + d->prop_em_width) <= d->width
       && nr - (ht + hb) > 5)
-    render_columns ();
+    render_columns (r, d, t, tw, th, hl, hr, ht, hb);
   else if (tw < d->width && th + d->cp_y < d->length)
-    render_simple ();
-  else 
-    render_segments ();
+    render_simple (r, d, t, tw, th, hl, hr, ht, hb);
+  else
+    render_segments (r, d, t, tw, th, hl, hr, ht, hb);
 
-  t->class->set_headers (hl, hr, ht, hb);
+  t->class->render_free (r);
 }
 
 /* Render the table into multiple columns. */
 static void
-render_columns (void)
+render_columns (void *r, struct outp_driver *d, struct som_entity *t,
+                int tw, int th UNUSED,
+                int hl UNUSED, int hr UNUSED, int ht, int hb)
 {
   int y0, y1;
   int max_len = 0;
   int index = 0;
-  
+  int nc, nr;
+  int cs;
+
+  t->class->count (t, &nc, &nr);
+  t->class->columns (t, &cs);
+
   assert (cs == SOM_COL_DOWN);
   assert (d->cp_x == 0);
 
   for (y0 = ht; y0 < nr - hb; y0 = y1)
     {
       int len;
-      
-      t->class->cumulate (SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
+
+      t->class->cumulate (r, SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
 
       if (y0 == y1)
        {
@@ -212,9 +296,10 @@ render_columns (void)
          if (len > max_len)
            max_len = len;
 
-         t->class->title (index++, 0);
-         t->class->render (0, y0, nc, y1);
-         
+         t->class->title (r, index++, 0, t->table_num, t->subtable_num,
+                           t->command_name);
+         t->class->render (r, 0, y0, nc, y1);
+
          d->cp_x += tw + 2 * d->prop_em_width;
          if (d->cp_x + tw > d->width)
            {
@@ -224,7 +309,7 @@ render_columns (void)
            }
        }
     }
-  
+
   if (d->cp_x > 0)
     {
       d->cp_x = 0;
@@ -234,44 +319,55 @@ render_columns (void)
 
 /* Render the table by itself on the current page. */
 static void
-render_simple (void)
+render_simple (void *r, struct outp_driver *d, struct som_entity *t,
+               int tw, int th,
+               int hl, int hr, int ht, int hb)
 {
+  int nc, nr;
+
+  t->class->count (t, &nc, &nr);
+
   assert (d->cp_x == 0);
   assert (tw < d->width && th + d->cp_y < d->length);
 
-  t->class->title (0, 0);
-  t->class->render (hl, ht, nc - hr, nr - hb);
+  t->class->title (r, 0, 0, t->table_num, t->subtable_num, t->command_name);
+  t->class->render (r, hl, ht, nc - hr, nr - hb);
   d->cp_y += th;
 }
 
 /* General table breaking routine. */
 static void
-render_segments (void)
+render_segments (void *r, struct outp_driver *d, struct som_entity *t,
+                 int tw UNUSED, int th UNUSED,
+                 int hl, int hr, int ht, int hb)
 {
   int count = 0;
-  
+
   int x_index;
   int x0, x1;
-  
+
+  int nc, nr;
+
   assert (d->cp_x == 0);
 
+  t->class->count (t, &nc, &nr);
   for (x_index = 0, x0 = hl; x0 < nc - hr; x0 = x1, x_index++)
     {
       int y_index;
       int y0, y1;
-      
-      t->class->cumulate (SOM_COLUMNS, x0, &x1, d->width, NULL);
+
+      t->class->cumulate (r, SOM_COLUMNS, x0, &x1, d->width, NULL);
       if (x_index == 0 && x1 != nc - hr)
        x_index++;
 
       for (y_index = 0, y0 = ht; y0 < nr - hb; y0 = y1, y_index++)
        {
          int len;
-      
+
          if (count++ != 0 && d->cp_y != 0)
            d->cp_y += d->font_height;
-             
-         t->class->cumulate (SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
+
+         t->class->cumulate (r, SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
          if (y_index == 0 && y1 != nr - hb)
            y_index++;
 
@@ -279,11 +375,14 @@ render_segments (void)
            {
              assert (d->cp_y);
              outp_eject_page (d);
-           } else {
-             t->class->title (x_index ? x_index : y_index,
-                              x_index ? y_index : 0);
-             t->class->render (x0, y0, x1, y1);
-         
+           }
+          else
+            {
+             t->class->title (r, x_index ? x_index : y_index,
+                              x_index ? y_index : 0,
+                               t->table_num, t->subtable_num, t->command_name);
+             t->class->render (r, x0, y0, x1, y1);
+
              d->cp_y += len;
            }
        }