Merge "master" into "output". fc11-i386-build29 fc11-x64-build26 lenny-x64-build50 sid-i386-build98
authorBen Pfaff <blp@gnu.org>
Sat, 24 Oct 2009 15:42:33 +0000 (08:42 -0700)
committerBen Pfaff <blp@gnu.org>
Sat, 24 Oct 2009 15:42:33 +0000 (08:42 -0700)
132 files changed:
INSTALL
NEWS
THANKS
acinclude.m4
config/devices
configure.ac
doc/configuring.texi
lib/automake.mk
perl-module/lib/PSPP.pm
src/data/caseproto.c
src/data/caseproto.h
src/data/casereader.c
src/data/casewindow.c
src/data/datasheet.c
src/data/dictionary.c
src/data/procedure.c
src/data/subcase.c
src/data/value-labels.c
src/data/value-labels.h
src/data/variable.c
src/language/automake.mk
src/language/command.c
src/language/control/repeat.c
src/language/data-io/data-parser.c
src/language/data-io/list.q
src/language/data-io/print.c
src/language/dictionary/split-file.c
src/language/dictionary/sys-file-info.c
src/language/lexer/variable-parser.c
src/language/stats/aggregate.c
src/language/stats/binomial.c
src/language/stats/chisquare.c
src/language/stats/correlations.c
src/language/stats/crosstabs.q
src/language/stats/descriptives.c
src/language/stats/examine.q
src/language/stats/frequencies.q
src/language/stats/npar-summary.c
src/language/stats/npar.q
src/language/stats/oneway.q
src/language/stats/regression.q
src/language/stats/reliability.q
src/language/stats/roc.c
src/language/stats/roc.h [new file with mode: 0644]
src/language/stats/sign.c
src/language/stats/t-test.q
src/language/stats/wilcoxon.c
src/language/syntax-file.c
src/language/syntax-string-source.c
src/language/utilities/echo.c
src/language/xforms/count.c
src/libpspp/abt.c
src/libpspp/abt.h
src/libpspp/bt.c
src/libpspp/bt.h
src/libpspp/cast.h [new file with mode: 0644]
src/libpspp/hash.h
src/libpspp/heap.h
src/libpspp/hmap.h
src/libpspp/ll.c
src/libpspp/ll.h
src/libpspp/llx.c
src/libpspp/range-map.h
src/libpspp/range-set.c
src/libpspp/range-set.h
src/libpspp/sparse-array.c
src/libpspp/str.c
src/libpspp/taint.c
src/libpspp/tmpfile.c
src/libpspp/tower.c
src/libpspp/tower.h
src/math/box-whisker.c
src/math/box-whisker.h
src/math/histogram.c
src/math/histogram.h
src/math/levene.c
src/math/np.c
src/math/np.h
src/math/order-stats.c
src/math/percentiles.c
src/math/percentiles.h
src/math/trimmed-mean.c
src/math/trimmed-mean.h
src/math/tukey-hinges.c
src/math/tukey-hinges.h
src/output/ascii.c
src/output/automake.mk
src/output/cairo.c [new file with mode: 0644]
src/output/cairo.h [new file with mode: 0644]
src/output/chart-provider.h [new file with mode: 0644]
src/output/chart.c
src/output/chart.h
src/output/charts/automake.mk [deleted file]
src/output/charts/barchart.c [deleted file]
src/output/charts/barchart.h [deleted file]
src/output/charts/box-whisker.c
src/output/charts/box-whisker.h
src/output/charts/cartesian.c
src/output/charts/cartesian.h
src/output/charts/dummy-chart.c [deleted file]
src/output/charts/np-plot.c [new file with mode: 0644]
src/output/charts/np-plot.h [new file with mode: 0644]
src/output/charts/piechart.c
src/output/charts/piechart.h
src/output/charts/plot-chart.c
src/output/charts/plot-chart.h
src/output/charts/plot-hist.c
src/output/charts/plot-hist.h
src/output/charts/roc-chart.c [new file with mode: 0644]
src/output/charts/roc-chart.h [new file with mode: 0644]
src/output/html.c
src/output/manager.c
src/output/manager.h
src/output/odt.c [new file with mode: 0644]
src/output/output.c
src/output/output.h
src/output/postscript.c
src/output/table.c
src/output/table.h
src/ui/automake.mk
src/ui/gui/automake.mk
src/ui/gui/executor.c
src/ui/gui/find-dialog.c
src/ui/gui/output-viewer.glade
src/ui/gui/psppire-output-window.c
src/ui/gui/psppire-output-window.h
src/ui/gui/psppire.c
src/ui/gui/syntax-editor-source.c
src/ui/gui/val-labs-dialog.c
src/ui/terminal/automake.mk
src/ui/terminal/read-line.c
tests/libpspp/range-set-test.c

diff --git a/INSTALL b/INSTALL
index 4f1bc84cef9b3c3eda18fc9165cdbc0f5e61d5aa..0f4f5c9687567dfaa9c322407387f889ed7d19be 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -33,12 +33,13 @@ The following packages are required to install PSPP:
       If you don't have a version already, you can install GNU
       libiconv (http://www.gnu.org/software/libiconv/).
 
-The following package is required to enable PSPP's graphing features.
-If you cannot arrange to install it, you must run `configure' with
---without-libplot.
+The following packages are required to enable PSPP's graphing
+features.  If you cannot arrange to install them, you must run
+`configure' with --without-cairo.
 
-    * libplot, from GNU plotutils
-      (http://www.gnu.org/software/plotutils/).
+    * Cairo (http://cairographics.org/), version 1.5 or later.
+
+    * Pango (http://www.pango.org/), version 1.22 or later.
 
 The following packages are required to enable PSPPIRE, the graphical
 user interface for PSPP.  If you cannot install them or do not wish to
@@ -209,14 +210,17 @@ suffix on their names by giving `configure' the  option
 Optional Features
 =================
 
-`--without-libplot'
-    Don't compile in support for charts (using libplot).  This is
-    useful if your system doesn't have the libplot library.
+`--without-cairo'
+    Don't compile in support for charts (using Cairo and Pango).  This
+    is useful if your system lacks these libraries.
 
 `--without-gui'
     Don't build the PSPPIRE gui.  Use this option if you only want to
     build the command line version of PSPP.
 
+    Cairo and Pango required to build the GUI, so --without-cairo
+    implies --without-gui.
+
 `--with-gui-tools'
     Build the gui developer tools.  There is no reason to use this
     option unless you're involved with the development of PSPP
diff --git a/NEWS b/NEWS
index 1aac688c8736a3b66322cf993fbc0f195ee9920f..5f952324f12229c7e3a110dfd688f7b0fa5e9c26 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,10 +1,17 @@
 PSPP NEWS -- history of user-visible changes.
-Time-stamp: <2009-09-08 21:08:29 blp>
+Time-stamp: <2009-10-24 08:12:04 blp>
 Copyright (C) 1996-9, 2000, 2008, 2009 Free Software Foundation, Inc.
 See the end for copying conditions.
 
 Please send PSPP bug reports to bug-gnu-pspp@gnu.org.
 
+Changes from 0.7.2 to 0.7.3:
+
+ * Charts are now produced with Cairo and Pango, instead of libplot.
+   Without them, the new graphing features will not work.  If you do
+   not have Cairo and Pango installed, you must run `configure' with
+   --without-cairo.
+
 Changes from 0.7.1 to 0.7.2:
 
  * Updated Perl module interface.
diff --git a/THANKS b/THANKS
index 5af731084b7a32f5e571832a10a4e2680609afca..b5bd839e9db0e18c3cba22f313d95c1b5ef2aec6 100644 (file)
--- a/THANKS
+++ b/THANKS
@@ -12,5 +12,3 @@ Thanks to...
      * François Pinard for advice on proceeding with development.
 
      * Jim Van Zandt for Debian packaging and suggestions.
-
-     * The authors of libplot and libgsl for providing those libraries.
index 307ce149a4344f3f5e373146cac3fefea7b5a08c..14d11dc70a26e44bc22f28bac8ca5418360c4843 100644 (file)
@@ -43,40 +43,6 @@ AC_DEFUN([PSPP_PERL],
   AC_SUBST([VERSION_FOR_PERL])
 ])
 
-dnl Check that libplot is available.
-AC_DEFUN([PSPP_LIBPLOT],
-[
-  AC_ARG_WITH(
-    libplot, 
-    [AS_HELP_STRING([--without-libplot],
-                    [don't compile in support of charts (using libplot)])])
-
-  if test x"$with_libplot" != x"no" ; then 
-    # Check whether we can link against libplot without any extra libraries.
-    AC_CHECK_LIB(plot, pl_newpl_r, [LIBPLOT_LIBS="-lplot"])
-
-    # Check whether we can link against libplot if we also link X.
-    if test x"$LIBPLOT_LIBS" = x""; then
-      AC_PATH_XTRA
-      extra_libs="-lXaw -lXmu -lXt $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS -lm"
-      AC_CHECK_LIB(plot, pl_newpl_r,
-                  [LIBPLOT_LIBS="-lplot $extra_libs"
-                    LDFLAGS="$LDFLAGS $X_LIBS"],,
-                  [$extra_libs])
-    fi
-
-    # Still can't link?
-    if test x"$LIBPLOT_LIBS" = x""; then
-      PSPP_REQUIRED_PREREQ([libplot (or use --without-libplot)])
-    fi
-
-    # Set up to make everything work.
-    LIBS="$LIBPLOT_LIBS $LIBS"
-    AC_DEFINE(HAVE_LIBPLOT, 1,
-              [Define to 1 if you have the `libplot' library (-lplot).])
-  fi
-])
-
 dnl PSPP_CHECK_CC_OPTION([OPTION], [ACTION-IF-ACCEPTED], [ACTION-IF-REJECTED])
 dnl Check whether the given C compiler OPTION is accepted.
 dnl If so, execute ACTION-IF-ACCEPTED, otherwise ACTION-IF-REJECTED.
index 48513fe6bc83899b127136dad62994f2a28cc72c..3bc685a004fb7eb2cdf871a4e696b89557f37651 100644 (file)
@@ -80,6 +80,13 @@ html:html::
 # PostScript device.
 list-ps:postscript::
 
+# Cairo devices.
+pdf:cairo:listing:
+svg:cairo:listing:output-type=svg output-file="pspp.svg"
+ps-cairo:cairo:listing:output-type=ps output-file="pspp-cairo.ps"
+
+odt:odf:listing:debug=off output-file="pspp.odt"
+
 # Devices that support the IBM PC line-drawing characters.
 define ibmpc-graphics \
   box[0000]='\x20' box[0001]='\xb3' box[0002]='\xba' \
@@ -115,6 +122,89 @@ tty-ibmpc:ascii:screen:length=$viewlength width=$viewwidth ${ibmpc-graphics} \
 list-ibmpc:ascii:listing:length=66 width=79 output-file=${list-output-file} \
   ${ibmpc-graphics}
 
+# Devices that support Unicode line-drawing characters in UTF-8 encoding.
+# PSPP doesn't support a \u escape but if it did then this is how these
+# would appear:
+#
+#     define utf8-graphics \
+#       box[0000]='\u0020' box[1000]='\u2576' box[2000]='\u2550' \
+#       box[0100]='\u2577' box[1100]='\u256D' box[2100]='\u2552' \
+#       box[0200]='\u2551' box[1200]='\u2553' box[2200]='\u2554' \
+#       box[0010]='\u2574' box[1010]='\u2500' box[2010]='\u2550' \
+#       box[0110]='\u256E' box[1110]='\u252C' box[2110]='\u2564' \
+#       box[0210]='\u2556' box[1210]='\u2565' box[2210]='\u2566' \
+#       box[0020]='\u2550' box[1020]='\u2550' box[2020]='\u2550' \
+#       box[0120]='\u2555' box[1120]='\u2564' box[2120]='\u2564' \
+#       box[0220]='\u2557' box[1220]='\u2566' box[2220]='\u2566' \
+#       box[0001]='\u2575' box[1001]='\u2570' box[2001]='\u2558' \
+#       box[0101]='\u2502' box[1101]='\u251C' box[2101]='\u255E' \
+#       box[0201]='\u2551' box[1201]='\u255F' box[2201]='\u2560' \
+#       box[0011]='\u256F' box[1011]='\u2534' box[2011]='\u2567' \
+#       box[0111]='\u2524' box[1111]='\u253C' box[2111]='\u256A' \
+#       box[0211]='\u2562' box[1211]='\u256B' box[2211]='\u256C' \
+#       box[0021]='\u255B' box[1021]='\u2567' box[2021]='\u2567' \
+#       box[0121]='\u2561' box[1121]='\u256A' box[2121]='\u256A' \
+#       box[0221]='\u2563' box[1221]='\u256C' box[2221]='\u256C' \
+#       box[0002]='\u2551' box[1002]='\u2559' box[2002]='\u255A' \
+#       box[0102]='\u2551' box[1102]='\u255F' box[2102]='\u2560' \
+#       box[0202]='\u2551' box[1202]='\u255F' box[2202]='\u2560' \
+#       box[0012]='\u255C' box[1012]='\u2568' box[2012]='\u2569' \
+#       box[0112]='\u2562' box[1112]='\u256A' box[2112]='\u256C' \
+#       box[0212]='\u2562' box[1212]='\u256B' box[2212]='\u256C' \
+#       box[0022]='\u255D' box[1022]='\u2569' box[2022]='\u2569' \
+#       box[0122]='\u2563' box[1122]='\u256C' box[2122]='\u256C' \
+#       box[0222]='\u2563' box[1222]='\u256C' box[2222]='\u256C'
+#
+# Instead, we encode them in UTF-8 by hand below.
+#
+# Here is a little Perl program that I used to do this translation:
+#
+#     sub utf8_encode {
+#         my $val = hex($_[0]);
+#         my $d0 = 0xe0 | ($val >> 12);
+#         my $d1 = 0x80 | (($val >> 6) & 0x3f);
+#         my $d2 = 0x80 | ($val & 0x3f);
+#         return sprintf('\x%02x\x%02x\x%02x', $d0, $d1, $d2);
+#     }
+#     while (<>) {
+#         s/\\u(....)/utf8_encode($1)/ge;
+#         print $_;
+#     }
+#
+define utf8-graphics \
+  box[0000]='\xe0\x80\xa0' box[1000]='\xe2\x95\xb6' box[2000]='\xe2\x95\x90' \
+  box[0100]='\xe2\x95\xb7' box[1100]='\xe2\x95\xad' box[2100]='\xe2\x95\x92' \
+  box[0200]='\xe2\x95\x91' box[1200]='\xe2\x95\x93' box[2200]='\xe2\x95\x94' \
+  box[0010]='\xe2\x95\xb4' box[1010]='\xe2\x94\x80' box[2010]='\xe2\x95\x90' \
+  box[0110]='\xe2\x95\xae' box[1110]='\xe2\x94\xac' box[2110]='\xe2\x95\xa4' \
+  box[0210]='\xe2\x95\x96' box[1210]='\xe2\x95\xa5' box[2210]='\xe2\x95\xa6' \
+  box[0020]='\xe2\x95\x90' box[1020]='\xe2\x95\x90' box[2020]='\xe2\x95\x90' \
+  box[0120]='\xe2\x95\x95' box[1120]='\xe2\x95\xa4' box[2120]='\xe2\x95\xa4' \
+  box[0220]='\xe2\x95\x97' box[1220]='\xe2\x95\xa6' box[2220]='\xe2\x95\xa6' \
+  box[0001]='\xe2\x95\xb5' box[1001]='\xe2\x95\xb0' box[2001]='\xe2\x95\x98' \
+  box[0101]='\xe2\x94\x82' box[1101]='\xe2\x94\x9c' box[2101]='\xe2\x95\x9e' \
+  box[0201]='\xe2\x95\x91' box[1201]='\xe2\x95\x9f' box[2201]='\xe2\x95\xa0' \
+  box[0011]='\xe2\x95\xaf' box[1011]='\xe2\x94\xb4' box[2011]='\xe2\x95\xa7' \
+  box[0111]='\xe2\x94\xa4' box[1111]='\xe2\x94\xbc' box[2111]='\xe2\x95\xaa' \
+  box[0211]='\xe2\x95\xa2' box[1211]='\xe2\x95\xab' box[2211]='\xe2\x95\xac' \
+  box[0021]='\xe2\x95\x9b' box[1021]='\xe2\x95\xa7' box[2021]='\xe2\x95\xa7' \
+  box[0121]='\xe2\x95\xa1' box[1121]='\xe2\x95\xaa' box[2121]='\xe2\x95\xaa' \
+  box[0221]='\xe2\x95\xa3' box[1221]='\xe2\x95\xac' box[2221]='\xe2\x95\xac' \
+  box[0002]='\xe2\x95\x91' box[1002]='\xe2\x95\x99' box[2002]='\xe2\x95\x9a' \
+  box[0102]='\xe2\x95\x91' box[1102]='\xe2\x95\x9f' box[2102]='\xe2\x95\xa0' \
+  box[0202]='\xe2\x95\x91' box[1202]='\xe2\x95\x9f' box[2202]='\xe2\x95\xa0' \
+  box[0012]='\xe2\x95\x9c' box[1012]='\xe2\x95\xa8' box[2012]='\xe2\x95\xa9' \
+  box[0112]='\xe2\x95\xa2' box[1112]='\xe2\x95\xaa' box[2112]='\xe2\x95\xac' \
+  box[0212]='\xe2\x95\xa2' box[1212]='\xe2\x95\xab' box[2212]='\xe2\x95\xac' \
+  box[0022]='\xe2\x95\x9d' box[1022]='\xe2\x95\xa9' box[2022]='\xe2\x95\xa9' \
+  box[0122]='\xe2\x95\xa3' box[1122]='\xe2\x95\xac' box[2122]='\xe2\x95\xac' \
+  box[0222]='\xe2\x95\xa3' box[1222]='\xe2\x95\xac' box[2222]='\xe2\x95\xac'
+
+tty-utf8:ascii:screen:length=$viewlength width=$viewwidth ${utf8-graphics} \
+  output-file=${tty-output-file} emphasis=none
+list-utf8:ascii:listing:length=66 width=79 output-file=${list-output-file} \
+  ${utf8-graphics} emphasis=none
+
 # Local Variables:
 # fill-prefix: "# "
 # End:
index 95dfc66f454604b1ee3c0a8d084511675a1860c5..499128b3c5e524aa46f2abb650cf5c43f4b642f4 100644 (file)
@@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script.
 
 dnl Initialize.
 AC_PREREQ(2.60)
-AC_INIT([pspp],[0.7.2],[bug-gnu-pspp@gnu.org])
+AC_INIT([pspp],[0.7.3],[bug-gnu-pspp@gnu.org])
 AC_CONFIG_HEADERS([config.h])
 AM_INIT_AUTOMAKE
 
@@ -40,27 +40,39 @@ fi
 dnl Checks for libraries.
 AC_SYS_LARGEFILE
 AC_SEARCH_LIBS([sin], [m])
-PSPP_LIBPLOT
 PSPP_LC_PAPER
-AM_CONDITIONAL(WITHCHARTS, test x"$with_libplot" != x"no")
 
 
 AC_ARG_VAR([PSPP_LDFLAGS], [linker flags to be used for linking the pspp binary only])
 AC_ARG_VAR([PSPPIRE_LDFLAGS], [linker flags to be used for linking the psppire binary only])
 
-
-AC_ARG_WITH(
-  gui, 
-  [AS_HELP_STRING([--without-gui], [don't build the PSPPIRE gui])])
-
-required_gtk_version=2.12
-
-if test x"$with_gui" != x"no" ; then 
-  PKG_CHECK_MODULES(GTK, gtk+-2.0 >= $required_gtk_version,,
-    [PSPP_REQUIRED_PREREQ([gtk+ 2.0 v$required_gtk_version or later (or use --without-gui)])])
+# Support for Cairo and Pango.
+AC_ARG_WITH([cairo],
+  [AS_HELP_STRING(
+    [--without-cairo], 
+    [Don't build support for charts (using Cairo and Pango);
+     implies --without-gui])],
+  [], [with_cairo=yes])
+AM_CONDITIONAL([HAVE_CAIRO], [test "$with_cairo" != no])
+if test "$with_cairo" != no; then
+  PKG_CHECK_MODULES([CAIRO], [cairo >= 1.5 pango >= 1.20 pangocairo], 
+    [CPPFLAGS="$CPPFLAGS $CAIRO_CFLAGS"
+     AC_DEFINE([HAVE_CAIRO], 1, 
+       [Define to 1 if Cairo and Pango are available.])],
+    [PSPP_REQUIRED_PREREQ([cairo 1.5 or later and pango 1.20 or later (or use --without-cairo)])])
 fi
-AM_CONDITIONAL(WITHGUI, test x"$with_gui" != x"no")
 
+# Support for GUI.
+AC_ARG_WITH([gui], 
+  [AS_HELP_STRING([--without-gui], 
+                  [Don't build the PSPPIRE GUI (using GTK+)])],
+  [], [with_gui=yes])
+AM_CONDITIONAL([HAVE_GUI], 
+               [test "$with_cairo" != no && test "$with_gui" != "no"])
+if test "$with_cairo" != no && test "$with_gui" != "no"; then
+  PKG_CHECK_MODULES([GTK], [gtk+-2.0 >= 2.12], [],
+    [PSPP_REQUIRED_PREREQ([gtk+ 2.0 version 2.12 or later (or use --without-gui)])])
+fi
 
 dnl Checks needed for psql reader
 
index 164d9ab1aac182a38c04120ef1028e8fb30684aa..a1007d921da87716f8015c8e751e3b99b90090ff 100644 (file)
@@ -11,9 +11,10 @@ This chapter describe how to configure PSPP for your system.
 * Configuration files::         How configuration files are read.
 * Environment variables::       All about environment variables.
 * Output devices::              Describing your terminal(s) and printer(s).
-* PostScript driver class::     Configuration of PostScript devices.
+* Cairo driver class::          Configuration of Cairo devices.
 * ASCII driver class::          Configuration of character-code devices.
 * HTML driver class::           Configuration for HTML output.
+* PostScript driver class::     Configuration of PostScript devices.
 * Miscellaneous configuring::   Even more configuration variables.
 @end menu
 
@@ -324,7 +325,7 @@ A unique identifier, used to determine whether to enable the driver.
 
 @item class name
 One of the predefined driver classes supported by PSPP.  The
-currently supported driver classes include `postscript' and `ascii'.
+currently supported driver classes include `cairo' and `ascii'.
 
 @item device type(s)
 Zero or more of the following keywords, delimited by spaces:
@@ -540,26 +541,27 @@ interpreted; only the lower 8 bits are used.
 Tokens, outside of quoted strings, are delimited by white space or equals
 signs.
 
-@node PostScript driver class
-@section The PostScript driver class
+@node Cairo driver class
+@section The Cairo driver class
 
-The @code{postscript} driver class is used to produce output that is
-acceptable to PostScript printers and other interpreters.
+The @code{cairo} driver class can produce output in PDF, PostScript,
+and SVG formats.  It has full support for international character
+sets.
+
+The Cairo driver is only available if your copy of PSPP was built with
+the Cairo library.
 
 The available options are listed below.
 
 @table @code
 @item output-file=@var{file-name}
 
-File to which output should be sent.  This can be an ordinary file name
-(i.e., @code{"pspp.ps"}), a pipe (i.e., @code{"|lpr"}), or
-stdout (@code{"-"}).  Default: @code{"pspp.ps"}.
+File to which output should be sent.  Default: @code{"pspp.pdf"}.
 
-@item headers=@var{boolean}
+@item output-type=@var{output-type}
 
-Controls whether the standard headers showing the time and date and
-title and subtitle are printed at the top of each page.  Default:
-@code{on}.
+Type of output to write to the output file, one of @code{pdf},
+@code{ps}, or @code{svg}.  Default: @code{pdf}.
 
 @item paper-size=@var{paper-size}
 
@@ -578,6 +580,12 @@ assumed.
 
 Either @code{portrait} or @code{landscape}.  Default: @code{portrait}.
 
+@item headers=@var{boolean}
+
+Controls whether the standard headers showing the time and date and
+title and subtitle are printed at the top of each page.  Default:
+@code{on}.
+
 @item left-margin=@var{dimension}
 @itemx right-margin=@var{dimension}
 @itemx top-margin=@var{dimension}
@@ -587,29 +595,17 @@ Sets the margins around the page.  The headers, if enabled, are not
 included in the margins; they are in addition to the margins.  For a
 description of dimensions, see @ref{Dimensions}.  Default: @code{0.5in}.
 
-@item prop-font=@var{afm-file}[,@var{font-file}[,@var{encoding-file}]]
-@itemx emph-font=@var{afm-file}[,@var{font-file}[,@var{encoding-file}]]
-@itemx fixed-font=@var{afm-file}[,@var{font-file}[,@var{encoding-file}]]
+@item prop-font=@var{font-name}
+@itemx emph-font=@var{font-name}
+@itemx fixed-font=@var{font-name}
 
 Sets the font used for proportional, emphasized, or fixed-pitch text.
-The only required value is @var{afm-file}, the AFM file for the font.
-
-If specified, @var{font-file} will be downloaded to the printer at the
-beginning of the print job.  The font file may be in PFA or PFB format.
-
-The font is reencoded as specified in @var{encoding-file}, if specified.
-Each line in @var{encoding-file} should consist of a PostScript
-character name and a decimal encoding value (between 0 and 255),
-separated by white space.  Blank lines and comments introduced by
-@samp{#} are also allowed.
+Most systems support CSS-like font names such as ``serif'' and
+``monospace'', but a wide range of system-specific font are likely to
+be supported as well.
 
-The files specified on these options are located as follows.  If
-the file name begins with @samp{/}, then it is taken as an absolute
-path.  Otherwise, PSPP searches its configuration path for the specified
-name prefixed by @code{psfonts/} (@pxref{File locations}).
-
-Default: proportional font @code{Times-Roman.afm}, emphasis font
-@code{Times-Italic.afm}, fixed-pitch font @code{Courier.afm}.
+Default: proportional font @code{serif}, emphasis font @code{serif
+italic}, fixed-pitch font @code{monospace}.
 
 @item font-size=@var{font-size}
 
@@ -653,14 +649,11 @@ a single @samp{#}, which is replaced by the chart number.  Default:
 @file{"pspp-#.png"}.
 
 @item chart-type=@var{type}.
-Type of charts to output.  Available types typically include @samp{X},
-@samp{png}, @samp{gif}, @samp{svg}, @samp{ps}, @samp{cgm}, @samp{fig},
-@samp{pcl}, @samp{hpgl}, @samp{regis}, @samp{tek}, and @samp{meta}.
+Type of charts to output, either @samp{png} or @samp{none}.
 Default: @samp{png}.
 
-You may specify @samp{none} to disable chart output.  Charts are also
-disabled if your installation of PSPP was compiled without
-@code{libplot}.
+Charts are always disabled if your installation of PSPP was compiled
+without the @code{cairo} library.
 
 @item paginate=@var{boolean}
 
@@ -812,6 +805,104 @@ format.  The name should contain a single @samp{#}, which is replaced by
 the chart number.  Default: @file{"pspp-#.png"}.
 @end table
 
+@node PostScript driver class
+@section The PostScript driver class
+
+The @code{postscript} driver class is used to produce output that is
+acceptable to PostScript printers and other interpreters.
+The PostScript driver class does not support charts.
+
+The PostScript driver class is deprecated.  It is likely to be removed
+in a future version of PSPP.  We suggest that you use the Cairo driver
+class instead, which can output PostScript as well and has better font
+support, including support for international character sets, and does
+support charts.
+
+The available options are listed below.
+
+@table @code
+@item output-file=@var{file-name}
+
+File to which output should be sent.  This can be an ordinary file name
+(i.e., @code{"pspp.ps"}), a pipe (i.e., @code{"|lpr"}), or
+stdout (@code{"-"}).  Default: @code{"pspp.ps"}.
+
+@item headers=@var{boolean}
+
+Controls whether the standard headers showing the time and date and
+title and subtitle are printed at the top of each page.  Default:
+@code{on}.
+
+@item paper-size=@var{paper-size}
+
+Paper size.  You may specify a name (e.g.@: @code{a4}, @code{letter})
+or measurements (e.g.@: @code{210x297}, @code{8.5x11in}).
+
+The default paper size is taken from the @env{PAPERSIZE} environment
+variable or the file indicated by the @env{PAPERCONF} environment
+variable, if either variable is set.  If not, and your system supports
+the @code{LC_PAPER} locale category, then the default paper size is
+taken from the locale.  Otherwise, if @file{/etc/papersize} exists,
+the default paper size is read from it.  As a last resort, A4 paper is
+assumed.
+
+@item orientation=@var{orientation}
+
+Either @code{portrait} or @code{landscape}.  Default: @code{portrait}.
+
+@item left-margin=@var{dimension}
+@itemx right-margin=@var{dimension}
+@itemx top-margin=@var{dimension}
+@itemx bottom-margin=@var{dimension}
+
+Sets the margins around the page.  The headers, if enabled, are not
+included in the margins; they are in addition to the margins.  For a
+description of dimensions, see @ref{Dimensions}.  Default: @code{0.5in}.
+
+@item prop-font=@var{afm-file}[,@var{font-file}[,@var{encoding-file}]]
+@itemx emph-font=@var{afm-file}[,@var{font-file}[,@var{encoding-file}]]
+@itemx fixed-font=@var{afm-file}[,@var{font-file}[,@var{encoding-file}]]
+
+Sets the font used for proportional, emphasized, or fixed-pitch text.
+The only required value is @var{afm-file}, the AFM file for the font.
+
+If specified, @var{font-file} will be downloaded to the printer at the
+beginning of the print job.  The font file may be in PFA or PFB format.
+
+The font is reencoded as specified in @var{encoding-file}, if specified.
+Each line in @var{encoding-file} should consist of a PostScript
+character name and a decimal encoding value (between 0 and 255),
+separated by white space.  Blank lines and comments introduced by
+@samp{#} are also allowed.
+
+The files specified on these options are located as follows.  If
+the file name begins with @samp{/}, then it is taken as an absolute
+path.  Otherwise, PSPP searches its configuration path for the specified
+name prefixed by @code{psfonts/} (@pxref{File locations}).
+
+Default: proportional font @code{Times-Roman.afm}, emphasis font
+@code{Times-Italic.afm}, fixed-pitch font @code{Courier.afm}.
+
+@item font-size=@var{font-size}
+
+Sets the size of the default fonts, in thousandths of a point.  Default:
+10000 (10 point).
+
+@item line-gutter=@var{dimension}
+
+Sets the width of white space on either side of lines that border text
+or graphics objects.  @xref{Dimensions}.  Default: @code{1pt}.
+
+@item line-spacing=@var{dimension}
+
+Sets the spacing between the lines in a double line in a table.
+Default: @code{1pt}.
+
+@item line-width=@var{dimension}
+
+Sets the width of the lines used in tables.  Default: @code{0.5pt}.
+@end table
+
 @node Miscellaneous configuring
 @section Miscellaneous configuration
 
index 6b20c67478909fe6232730b58d7b4056de720490..9bde4afcf26d77b9950e960cdfd29063b7cbbbeb 100644 (file)
@@ -2,7 +2,7 @@
 
 include $(top_srcdir)/lib/linreg/automake.mk
 
-if WITHGUI
+if HAVE_GUI
 include $(top_srcdir)/lib/gtk-contrib/automake.mk
 endif
 
index e5599908b683f7b6e6bde716fbfa9f74d3460338..c6b059826f0c870b5082634a4351f9dd7b833dc1 100644 (file)
@@ -21,7 +21,7 @@ None by default.
 
 =cut
 BEGIN {
-       $PSPP::VERSION='0.7.2';
+       $PSPP::VERSION='0.7.3';
        require XSLoader;
        XSLoader::load('PSPP', $PSPP::VERSION);
 }
index 1a40213a537d295f78241a0c35a0594ba3a6262a..9837013acb8bee86cb6d30bf0876a706a05dbc71 100644 (file)
@@ -320,7 +320,7 @@ caseproto_free__ (struct caseproto *proto)
 void
 caseproto_refresh_long_string_cache__ (const struct caseproto *proto_)
 {
-  struct caseproto *proto = (struct caseproto *) proto_;
+  struct caseproto *proto = CONST_CAST (struct caseproto *, proto_);
   size_t n, i;
 
   assert (proto->long_strings == NULL);
index b85a9f32d9260f02d6339b8f0852caca8518722f..b0f45c418c47aae8017b0469a5307cff6b9cd7f6 100644 (file)
@@ -22,6 +22,7 @@
 #include <stddef.h>
 #include <stdlib.h>
 #include <data/value.h>
+#include <libpspp/cast.h>
 #include <libpspp/compiler.h>
 
 /* Case prototype.
@@ -144,7 +145,7 @@ void caseproto_free__ (struct caseproto *);
 static inline struct caseproto *
 caseproto_ref (const struct caseproto *proto_)
 {
-  struct caseproto *proto = (struct caseproto *) proto_;
+  struct caseproto *proto = CONST_CAST (struct caseproto *, proto_);
   proto->ref_cnt++;
   return proto;
 }
index a1550ac57560e5fb623d12ede14b7e1a65ca661b..5ae5a0a8ea4431019660c60e1d56d9d68ed8c3fa 100644 (file)
@@ -108,7 +108,7 @@ casereader_destroy (struct casereader *reader)
 struct casereader *
 casereader_clone (const struct casereader *reader_)
 {
-  struct casereader *reader = (struct casereader *) reader_;
+  struct casereader *reader = CONST_CAST (struct casereader *, reader_);
   struct casereader *clone;
   if ( reader == NULL ) 
     return NULL;
index 9b04b9413c61bbace1e5b189d68e65eebe152482..936b6aa940051be14a422fde8a8773f22388eeb7 100644 (file)
@@ -168,7 +168,7 @@ casewindow_pop_tail (struct casewindow *cw, casenumber case_cnt)
 struct ccase *
 casewindow_get_case (const struct casewindow *cw_, casenumber case_idx)
 {
-  struct casewindow *cw = (struct casewindow *) cw_;
+  struct casewindow *cw = CONST_CAST (struct casewindow *, cw_);
 
   assert (case_idx >= 0 && case_idx < casewindow_get_case_cnt (cw));
   if (casewindow_error (cw))
index fa24d8ce77014e980cb79e4700ffc63832c5d061..4abc526f0cb2a720cb8e3023eb7d732effdb5da4 100644 (file)
@@ -275,7 +275,7 @@ datasheet_destroy (struct datasheet *ds)
 const struct caseproto *
 datasheet_get_proto (const struct datasheet *ds_)
 {
-  struct datasheet *ds = (struct datasheet *) ds_;
+  struct datasheet *ds = CONST_CAST (struct datasheet *, ds_);
   if (ds->proto == NULL)
     {
       size_t i;
@@ -548,7 +548,7 @@ datasheet_get_row (const struct datasheet *ds, casenumber row)
 {
   size_t n_columns = datasheet_get_n_columns (ds);
   struct ccase *c = case_create (datasheet_get_proto (ds));
-  if (rw_case ((struct datasheet *) ds, OP_READ,
+  if (rw_case (CONST_CAST (struct datasheet *, ds), OP_READ,
                row, 0, n_columns, case_data_all_rw (c)))
     return c;
   else
@@ -582,7 +582,8 @@ datasheet_get_value (const struct datasheet *ds, casenumber row,
                      size_t column, union value *value)
 {
   assert (row >= 0);
-  return rw_case ((struct datasheet *) ds, OP_READ, row, column, 1, value);
+  return rw_case (CONST_CAST (struct datasheet *, ds), OP_READ,
+                  row, column, 1, value);
 }
 
 /* Stores VALUE into DS in the given ROW and COLUMN.  VALUE must
index ab653d874193a46d1f9a83a03f1e15ac4e09e7a6..1ddf5575aa68c4877f77fea845d3b4e6ea567ccc 100644 (file)
@@ -1016,7 +1016,7 @@ dict_set_case_limit (struct dictionary *d, casenumber case_limit)
 const struct caseproto *
 dict_get_proto (const struct dictionary *d_)
 {
-  struct dictionary *d = (struct dictionary *) d_;
+  struct dictionary *d = CONST_CAST (struct dictionary *, d_);
   if (d->proto == NULL)
     {
       size_t i;
@@ -1375,7 +1375,7 @@ dict_clear_vectors (struct dictionary *d)
 struct attrset *
 dict_get_attributes (const struct dictionary *d) 
 {
-  return (struct attrset *) &d->attributes;
+  return CONST_CAST (struct attrset *, &d->attributes);
 }
 
 /* Replaces D's attributes set by a copy of ATTRS. */
index b762214d10e20a33d6bbdf89ac52253a67589453..5e013a14af767d80a097a4c46f577ed77c4c3210 100644 (file)
@@ -682,7 +682,7 @@ dataset_end_of_command (struct dataset *ds)
       else
         {
           const struct taint *taint = casereader_get_taint (ds->source);
-          taint_reset_successor_taint ((struct taint *) taint);
+          taint_reset_successor_taint (CONST_CAST (struct taint *, taint));
           assert (!taint_has_tainted_successor (taint));
         }
     }
index 6ffaa4c2bed24a603e3ce0aa267c16661e82f471..03098685e86d338164ad8963f3f331bc24abb94d 100644 (file)
@@ -139,7 +139,7 @@ subcase_add (struct subcase *sc, int case_index, int width,
 const struct caseproto *
 subcase_get_proto (const struct subcase *sc_)
 {
-  struct subcase *sc = (struct subcase *) sc_;
+  struct subcase *sc = CONST_CAST (struct subcase *, sc_);
 
   if (sc->proto == NULL)
     {
index c8061f7bd5d7fee54c47f5e65aa739b442f10dce..0fe829cebe1781c4ccc2d791756d534b88153e0c 100644 (file)
@@ -24,6 +24,7 @@
 #include <data/value.h>
 #include <data/variable.h>
 #include <libpspp/array.h>
+#include <libpspp/cast.h>
 #include <libpspp/compiler.h>
 #include <libpspp/hash-functions.h>
 #include <libpspp/hmap.h>
@@ -169,7 +170,7 @@ void
 val_labs_replace (struct val_labs *vls, const union value *value,
                   const char *label)
 {
-  struct val_lab *vl = (struct val_lab *) val_labs_lookup (vls, value);
+  struct val_lab *vl = val_labs_lookup (vls, value);
   if (vl != NULL)
     {
       atom_destroy (vl->label);
@@ -181,9 +182,8 @@ val_labs_replace (struct val_labs *vls, const union value *value,
 
 /* Removes LABEL from VLS. */
 void
-val_labs_remove (struct val_labs *vls, const struct val_lab *label_)
+val_labs_remove (struct val_labs *vls, struct val_lab *label)
 {
-  struct val_lab *label = (struct val_lab *) label_;
   hmap_delete (&vls->labels, &label->node);
   value_destroy (&label->value, vls->width);
   atom_destroy (label->label);
@@ -203,7 +203,7 @@ val_labs_find (const struct val_labs *vls, const union value *value)
 /* Searches VLS for a value label for VALUE.  If successful,
    returns the value label; otherwise, returns a null pointer.
    Returns a null pointer if VLS is null. */
-const struct val_lab *
+struct val_lab *
 val_labs_lookup (const struct val_labs *vls, const union value *value)
 {
   if (vls != NULL)
index 53d13a389700eb586af6aaf14bd82bd540311235..460ab84c17c919d821c4c477a2c3fd033b520bbb 100644 (file)
@@ -70,7 +70,7 @@ size_t val_labs_count (const struct val_labs *);
 
 /* Looking up value labels. */
 const char *val_labs_find (const struct val_labs *, const union value *);
-const struct val_lab *val_labs_lookup (const struct val_labs *,
+struct val_lab *val_labs_lookup (const struct val_labs *,
                                        const union value *);
 
 /* Basic properties. */
@@ -82,7 +82,7 @@ void val_labs_set_width (struct val_labs *, int new_width);
 /* Adding value labels. */
 bool val_labs_add (struct val_labs *, const union value *, const char *);
 void val_labs_replace (struct val_labs *, const union value *, const char *);
-void val_labs_remove (struct val_labs *, const struct val_lab *);
+void val_labs_remove (struct val_labs *, struct val_lab *);
 
 /* Iterating through value labels. */
 const struct val_lab *val_labs_first (const struct val_labs *);
index d1e308640d05268df79f8639848cef04c618ba23..05edc57e2db85a3c02df4fbe3a16a6747d92c4bc 100644 (file)
@@ -957,7 +957,7 @@ void *
 var_attach_aux (const struct variable *v_,
                 void *aux, void (*aux_dtor) (struct variable *))
 {
-  struct variable *v = (struct variable *) v_ ; /* cast away const  */
+  struct variable *v = CONST_CAST (struct variable *, v_);
   assert (v->aux == NULL);
   assert (aux != NULL);
   v->aux = aux;
@@ -1015,7 +1015,7 @@ var_get_obs_vals (const struct variable *v)
 void
 var_set_obs_vals (const struct variable *v_, struct cat_vals *cat_vals)
 {
-  struct variable *v = (struct variable *) v_ ; /* cast away const */
+  struct variable *v = CONST_CAST (struct variable *, v_ );
   cat_stored_values_destroy (v->obs_vals);
   v->obs_vals = cat_vals;
 }
@@ -1035,7 +1035,7 @@ var_has_obs_vals (const struct variable *v)
 struct attrset *
 var_get_attributes (const struct variable *v) 
 {
-  return (struct attrset *) &v->attributes;
+  return CONST_CAST (struct attrset *, &v->attributes);
 }
 
 /* Replaces variable V's attributes set by a copy of ATTRS. */
index 4210b7fce45f8f05153f9fd4bd2cd28881255b01..3052b52353ca54416ffbb53f66c4354787bf6924 100644 (file)
@@ -13,9 +13,6 @@ include $(top_srcdir)/src/language/expressions/automake.mk
 noinst_LTLIBRARIES +=  src/language/liblanguage.la
 
 
-src_language_liblanguage_la_LIBADD = \
-       src/output/charts/libcharts.la
-
 src_language_liblanguage_la_SOURCES = \
        src/language/syntax-file.c \
        src/language/syntax-file.h \
index 32dba0b4f7cd0ca7760f18315e13ae0b845069a0..4c3612fb290977674247b7bd2d66801afa56f818 100644 (file)
@@ -37,7 +37,6 @@
 #include <libpspp/message.h>
 #include <libpspp/str.h>
 #include <output/manager.h>
-#include <output/table.h>
 #include <libpspp/getl.h>
 
 #if HAVE_SYS_WAIT_H
@@ -231,9 +230,9 @@ do_parse_command (struct lexer *lexer,
 
   /* Execute command. */
   msg_set_command_name (command->name);
-  tab_set_command_name (command->name);
+  som_set_command_name (command->name);
   result = command->function (lexer, ds);
-  tab_set_command_name (NULL);
+  som_set_command_name (NULL);
   msg_set_command_name (NULL);
 
   assert (cmd_result_is_valid (result));
index 32847bb4a9298bd100f0cb80e8489097adc7d8ca..cb608f769d5d4b91adbb5c21fa5ae3d4403a2c08 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+   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
@@ -29,6 +29,7 @@
 #include <language/command.h>
 #include <language/lexer/lexer.h>
 #include <language/lexer/variable-parser.h>
+#include <libpspp/cast.h>
 #include <libpspp/ll.h>
 #include <libpspp/message.h>
 #include <libpspp/misc.h>
@@ -512,10 +513,10 @@ find_substitution (struct repeat_block *block, struct substring name)
 /* Makes appropriate DO REPEAT macro substitutions within the
    repeated lines. */
 static void
-do_repeat_filter (struct getl_interface *block_,
-                  struct string *line)
+do_repeat_filter (struct getl_interface *interface, struct string *line)
 {
-  struct repeat_block *block = (struct repeat_block *) block_;
+  struct repeat_block *block
+    = UP_CAST (interface, struct repeat_block, parent);
   bool in_apos, in_quote, dot;
   struct substring input;
   struct string output;
@@ -557,7 +558,8 @@ do_repeat_filter (struct getl_interface *block_,
 static struct repeat_line *
 current_line (const struct getl_interface *interface)
 {
-  struct repeat_block *block = (struct repeat_block *) interface;
+  struct repeat_block *block
+    = UP_CAST (interface, struct repeat_block, parent);
   return (block->cur_line != ll_null (&block->lines)
           ? ll_data (block->cur_line, struct repeat_line, ll)
           : NULL);
@@ -570,7 +572,8 @@ static bool
 do_repeat_read  (struct getl_interface *interface,
                  struct string *output)
 {
-  struct repeat_block *block = (struct repeat_block *) interface;
+  struct repeat_block *block
+    = UP_CAST (interface, struct repeat_block, parent);
   struct repeat_line *line;
 
   block->cur_line = ll_next (block->cur_line);
@@ -591,9 +594,10 @@ do_repeat_read  (struct getl_interface *interface,
 /* Frees a DO REPEAT block.
    Called by getl to close out the DO REPEAT block. */
 static void
-do_repeat_close (struct getl_interface *block_)
+do_repeat_close (struct getl_interface *interface)
 {
-  struct repeat_block *block = (struct repeat_block *) block_;
+  struct repeat_block *block
+    = UP_CAST (interface, struct repeat_block, parent);
   pool_destroy (block->pool);
 }
 
index 020f8e4c7e5a20a0caabc4add9f228f98937d1b6..fe78aeb36ed272f309481870d79ea6669d48d735 100644 (file)
@@ -644,8 +644,8 @@ dump_fixed_table (const struct data_parser *parser,
   struct tab_table *t;
   size_t i;
 
-  t = tab_create (4, parser->field_cnt + 1, 0);
-  tab_columns (t, TAB_COL_DOWN, 1);
+  t = tab_create (4, parser->field_cnt + 1);
+  tab_columns (t, TAB_COL_DOWN);
   tab_headers (t, 0, 0, 1, 0);
   tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Variable"));
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Record"));
@@ -653,7 +653,7 @@ dump_fixed_table (const struct data_parser *parser,
   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Format"));
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 3, parser->field_cnt);
   tab_hline (t, TAL_2, 0, 3, 1);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   for (i = 0; i < parser->field_cnt; i++)
     {
@@ -685,14 +685,14 @@ dump_delimited_table (const struct data_parser *parser,
   struct tab_table *t;
   size_t i;
 
-  t = tab_create (2, parser->field_cnt + 1, 0);
-  tab_columns (t, TAB_COL_DOWN, 1);
+  t = tab_create (2, parser->field_cnt + 1);
+  tab_columns (t, TAB_COL_DOWN);
   tab_headers (t, 0, 0, 1, 0);
   tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Variable"));
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Format"));
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, parser->field_cnt);
   tab_hline (t, TAL_2, 0, 1, 1);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   for (i = 0; i < parser->field_cnt; i++)
     {
index c3f9b0881b959fac98a3cffac628662ffe143299..614fa1271e71b5ed3180e2f4e98e1b454745b769 100644 (file)
@@ -35,7 +35,7 @@
 #include <language/dictionary/split-file.h>
 #include <language/lexer/lexer.h>
 #include <libpspp/compiler.h>
-#include <libpspp/message.h>
+#include <libpspp/ll.h>
 #include <libpspp/message.h>
 #include <libpspp/misc.h>
 #include <output/htmlP.h>
 /* (functions) */
 
 /* Layout for one output driver. */
-struct list_ext
+struct list_target
   {
+    struct ll ll;
+    struct outp_driver *driver;
     int type;          /* 0=Values and labels fit across the page. */
     size_t n_vertical; /* Number of labels to list vertically. */
     size_t header_rows;        /* Number of header rows. */
@@ -84,11 +86,12 @@ static void write_line (struct outp_driver *d, const char *s);
 
 /* Other functions. */
 static void list_case (const struct ccase *, casenumber case_idx,
-                       const struct dataset *);
-static void determine_layout (void);
-static void clean_up (void);
-static void write_header (struct outp_driver *);
-static void write_all_headers (struct casereader *, const struct dataset*);
+                       const struct dataset *, struct ll_list *targets);
+static void determine_layout (struct ll_list *targets);
+static void clean_up (struct ll_list *targets);
+static void write_header (struct list_target *);
+static void write_all_headers (struct casereader *, const struct dataset *,
+                               struct ll_list *targets);
 
 /* Returns the number of text lines that can fit on the remainder of
    the page. */
@@ -136,6 +139,7 @@ cmd_list (struct lexer *lexer, struct dataset *ds)
   struct variable *casenum_var = NULL;
   struct casegrouper *grouper;
   struct casereader *group;
+  struct ll_list targets;
   casenumber case_idx;
   bool ok;
 
@@ -229,7 +233,7 @@ cmd_list (struct lexer *lexer, struct dataset *ds)
       cmd.v_variables[0] = casenum_var;
     }
 
-  determine_layout ();
+  determine_layout (&targets);
 
   case_idx = 0;
   for (grouper = casegrouper_create_splits (proc_open (ds), dict);
@@ -238,13 +242,13 @@ cmd_list (struct lexer *lexer, struct dataset *ds)
     {
       struct ccase *c;
 
-      write_all_headers (group, ds);
+      write_all_headers (group, ds, &targets);
       for (; (c = casereader_read (group)) != NULL; case_unref (c))
         {
           case_idx++;
           if (case_idx >= cmd.first && case_idx <= cmd.last
               && (case_idx - cmd.first) % cmd.step == 0)
-            list_case (c, case_idx, ds);
+            list_case (c, case_idx, ds, &targets);
         }
     }
   ok = casegrouper_destroy (grouper);
@@ -252,7 +256,7 @@ cmd_list (struct lexer *lexer, struct dataset *ds)
 
   ds_destroy(&line_buffer);
 
-  clean_up ();
+  clean_up (&targets);
 
   var_destroy (casenum_var);
 
@@ -262,9 +266,10 @@ cmd_list (struct lexer *lexer, struct dataset *ds)
 /* Writes headers to all devices.  This is done at the beginning of
    each SPLIT FILE group. */
 static void
-write_all_headers (struct casereader *input, const struct dataset *ds)
+write_all_headers (struct casereader *input, const struct dataset *ds,
+                   struct ll_list *targets)
 {
-  struct outp_driver *d;
+  struct list_target *target;
   struct ccase *c;
 
   c = casereader_peek (input, 0);
@@ -273,12 +278,13 @@ write_all_headers (struct casereader *input, const struct dataset *ds)
   output_split_file_values (ds, c);
   case_unref (c);
 
-  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
+  ll_for_each (target, struct list_target, ll, targets)
     {
+      struct outp_driver *d = target->driver;
       if (!d->class->special)
        {
          d->cp_y += d->font_height;            /* Blank line. */
-         write_header (d);
+         write_header (target);
        }
       else if (d->class == &html_class)
        {
@@ -304,38 +310,38 @@ write_all_headers (struct casereader *input, const struct dataset *ds)
 /* Writes the headers.  Some of them might be vertical; most are
    probably horizontal. */
 static void
-write_header (struct outp_driver *d)
+write_header (struct list_target *target)
 {
-  struct list_ext *prc = d->prc;
+  struct outp_driver *d = target->driver;
 
-  if (!prc->header_rows)
+  if (d->class->special || !target->header_rows)
     return;
 
-  if (n_lines_remaining (d) < prc->header_rows + 1)
+  if (n_lines_remaining (d) < target->header_rows + 1)
     {
       outp_eject_page (d);
-      assert (n_lines_remaining (d) >= prc->header_rows + 1);
+      assert (n_lines_remaining (d) >= target->header_rows + 1);
     }
 
   /* Design the header. */
-  if (!prc->header)
+  if (!target->header)
     {
       size_t i;
       size_t x;
 
       /* Allocate, initialize header. */
-      prc->header = xnmalloc (prc->header_rows, sizeof *prc->header);
+      target->header = xnmalloc (target->header_rows, sizeof *target->header);
       {
        int w = n_chars_width (d);
-       for (i = 0; i < prc->header_rows; i++)
+       for (i = 0; i < target->header_rows; i++)
          {
-           prc->header[i] = xmalloc (w + 1);
-           memset (prc->header[i], ' ', w);
+           target->header[i] = xmalloc (w + 1);
+           memset (target->header[i], ' ', w);
          }
       }
 
       /* Put in vertical names. */
-      for (i = x = 0; i < prc->n_vertical; i++)
+      for (i = x = 0; i < target->n_vertical; i++)
        {
          const struct variable *v = cmd.v_variables[i];
           const char *name = var_get_name (v);
@@ -343,10 +349,10 @@ write_header (struct outp_driver *d)
           const struct fmt_spec *print = var_get_print_format (v);
          size_t j;
 
-         memset (&prc->header[prc->header_rows - 1][x], '-', print->w);
+         memset (&target->header[target->header_rows - 1][x], '-', print->w);
          x += print->w - 1;
          for (j = 0; j < name_len; j++)
-           prc->header[name_len - j - 1][x] = name[j];
+           target->header[name_len - j - 1][x] = name[j];
          x += 2;
        }
 
@@ -358,21 +364,21 @@ write_header (struct outp_driver *d)
           size_t name_len = strlen (name);
           const struct fmt_spec *print = var_get_print_format (v);
 
-         memset (&prc->header[prc->header_rows - 1][x], '-',
+         memset (&target->header[target->header_rows - 1][x], '-',
                  MAX (print->w, (int) name_len));
          if ((int) name_len < print->w)
            x += print->w - name_len;
-         memcpy (&prc->header[0][x], name, name_len);
+         memcpy (&target->header[0][x], name, name_len);
          x += name_len + 1;
        }
 
       /* Add null bytes. */
-      for (i = 0; i < prc->header_rows; i++)
+      for (i = 0; i < target->header_rows; i++)
        {
          for (x = n_chars_width (d); x >= 1; x--)
-           if (prc->header[i][x - 1] != ' ')
+           if (target->header[i][x - 1] != ' ')
              {
-               prc->header[i][x] = 0;
+               target->header[i][x] = 0;
                break;
              }
          assert (x);
@@ -380,49 +386,52 @@ write_header (struct outp_driver *d)
     }
 
   /* Write out the header, in back-to-front order except for the last line. */
-  if (prc->header_rows >= 2)
+  if (target->header_rows >= 2)
     {
       size_t i;
 
-      for (i = prc->header_rows - 1; i-- != 0; )
-        write_line (d, prc->header[i]);
+      for (i = target->header_rows - 1; i-- != 0; )
+        write_line (d, target->header[i]);
     }
-  write_line (d, prc->header[prc->header_rows - 1]);
+  write_line (d, target->header[target->header_rows - 1]);
 }
 
 
 /* Frees up all the memory we've allocated. */
 static void
-clean_up (void)
+clean_up (struct ll_list *targets)
 {
-  struct outp_driver *d;
+  struct list_target *target, *next;
 
-  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
-    if (d->class->special == 0)
-      {
-       struct list_ext *prc = d->prc;
-       size_t i;
-
-       if (prc->header)
-         {
-           for (i = 0; i < prc->header_rows; i++)
-             free (prc->header[i]);
-           free (prc->header);
-         }
-       free (prc);
-      }
-    else if (d->class == &html_class)
-      {
-       if (d->page_open)
-         {
-           struct html_driver_ext *x = d->ext;
+  ll_for_each_safe (target, next, struct list_target, ll, targets)
+    {
+      struct outp_driver *d = target->driver;
+      if (d->class->special == 0)
+        {
+          if (target->header)
+            {
+              size_t i;
+              for (i = 0; i < target->header_rows; i++)
+                free (target->header[i]);
+              free (target->header);
+            }
+        }
+      else if (d->class == &html_class)
+        {
+          if (d->page_open)
+            {
+              struct html_driver_ext *x = d->ext;
 
-           fputs ("</TABLE>\n", x->file);
-         }
-      }
-    else
-      NOT_REACHED ();
+              fputs ("</TABLE>\n", x->file);
+            }
+        }
+      else
+        NOT_REACHED ();
 
+      ll_remove (&target->ll);
+      free (target);
+    }
+  
   free (cmd.v_variables);
 }
 
@@ -541,7 +550,7 @@ write_fallback_headers (struct outp_driver *d)
    This is complicated by the fact that we have to do all this for
    every output driver, not just once.  */
 static void
-determine_layout (void)
+determine_layout (struct ll_list *targets)
 {
   struct outp_driver *d;
 
@@ -549,6 +558,7 @@ determine_layout (void)
      size buffer to allocate. */
   int largest_page_width = 0;
 
+  ll_init (targets);
   for (d = outp_drivers (NULL); d; d = outp_drivers (d))
     {
       size_t column;   /* Current column. */
@@ -556,11 +566,17 @@ determine_layout (void)
       int height;       /* Height of vertical names. */
       int max_width;   /* Page width. */
 
-      struct list_ext *prc;
+      struct list_target *target;
+
+      target = xmalloc (sizeof *target);
+      ll_push_tail (targets, &target->ll);
+      target->driver = d;
+      target->type = 0;
+      target->n_vertical = 0;
+      target->header = NULL;
 
       if (d->class == &html_class)
        continue;
-
       assert (d->class->special == 0);
 
       outp_open_page (d);
@@ -568,11 +584,6 @@ determine_layout (void)
       max_width = n_chars_width (d);
       largest_page_width = MAX (largest_page_width, max_width);
 
-      prc = d->prc = xmalloc (sizeof *prc);
-      prc->type = 0;
-      prc->n_vertical = 0;
-      prc->header = NULL;
-
       /* Try layout #1. */
       for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
        {
@@ -583,7 +594,7 @@ determine_layout (void)
        }
       if (width <= max_width)
        {
-         prc->header_rows = 2;
+         target->header_rows = 2;
          continue;
        }
 
@@ -605,7 +616,7 @@ determine_layout (void)
       if (width <= max_width && height <= SHORT_NAME_LEN)
        {
 #ifndef NDEBUG
-         prc->n_vertical = SIZE_MAX;
+         target->n_vertical = SIZE_MAX;
 #endif
          for (column = cmd.n_variables; column-- != 0; )
            {
@@ -615,30 +626,30 @@ determine_layout (void)
              int trial_width = width - fmt_width + MAX (fmt_width, name_len);
              if (trial_width > max_width)
                {
-                 prc->n_vertical = column + 1;
+                 target->n_vertical = column + 1;
                  break;
                }
              width = trial_width;
            }
-         assert (prc->n_vertical != SIZE_MAX);
+         assert (target->n_vertical != SIZE_MAX);
 
-         prc->n_vertical = cmd.n_variables;
+         target->n_vertical = cmd.n_variables;
          /* Finally determine the length of the headers. */
-         for (prc->header_rows = 0, column = 0;
-              column < prc->n_vertical;
+         for (target->header_rows = 0, column = 0;
+              column < target->n_vertical;
               column++)
             {
               const struct variable *var = cmd.v_variables[column];
               size_t name_len = strlen (var_get_name (var));
-              prc->header_rows = MAX (prc->header_rows, name_len);
+              target->header_rows = MAX (target->header_rows, name_len);
             }
-         prc->header_rows++;
+         target->header_rows++;
          continue;
        }
 
       /* Otherwise use the ugly fallback listing format. */
-      prc->type = 1;
-      prc->header_rows = 0;
+      target->type = 1;
+      target->header_rows = 0;
 
       d->cp_y += d->font_height;
       write_fallback_headers (d);
@@ -651,122 +662,123 @@ determine_layout (void)
 /* Writes case C to output. */
 static void
 list_case (const struct ccase *c, casenumber case_idx,
-           const struct dataset *ds)
+           const struct dataset *ds, struct ll_list *targets)
 {
   struct dictionary *dict = dataset_dict (ds);
-  struct outp_driver *d;
+  const char *encoding = dict_get_encoding (dict);
+  struct list_target *target;
 
-  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
-    if (d->class->special == 0)
-      {
-       const struct list_ext *prc = d->prc;
-       const int max_width = n_chars_width (d);
-       int column;
-
-       if (!prc->header_rows)
-         {
-           ds_put_format(&line_buffer, "%8s: ",
-                          var_get_name (cmd.v_variables[0]));
-         }
-
-
-       for (column = 0; column < cmd.n_variables; column++)
-         {
-           const struct variable *v = cmd.v_variables[column];
-            const struct fmt_spec *print = var_get_print_format (v);
-           int width;
-
-           if (prc->type == 0 && column >= prc->n_vertical)
-              {
-                int name_len = strlen (var_get_name (v));
-                width = MAX (name_len, print->w);
-              }
-           else
-             width = print->w;
-
-           if (width + ds_length(&line_buffer) > max_width &&
-               ds_length(&line_buffer) != 0)
-             {
-               if (!n_lines_remaining (d))
-                 {
-                   outp_eject_page (d);
-                   write_header (d);
-                 }
+  ll_for_each (target, struct list_target, ll, targets)
+    {
+      struct outp_driver *d = target->driver;
 
-               write_line (d, ds_cstr (&line_buffer));
-               ds_clear(&line_buffer);
+      if (d->class->special == 0)
+        {
+          const int max_width = n_chars_width (d);
+          int column;
 
-               if (!prc->header_rows)
-                  ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
-             }
+          if (!target->header_rows)
+            {
+              ds_put_format(&line_buffer, "%8s: ",
+                            var_get_name (cmd.v_variables[0]));
+            }
 
-           if (width > print->w)
-              ds_put_char_multiple(&line_buffer, ' ', width - print->w);
 
-            if (fmt_is_string (print->type)
-                || dict_contains_var (dict, v))
-             {
-               char *s = data_out (case_data (c, v), dict_get_encoding (dict), print);
-               ds_put_cstr (&line_buffer, s);
-               free (s);
-             }
-            else
-              {
-               char *s;
-                union value case_idx_value;
-                case_idx_value.f = case_idx;
-                s = data_out (&case_idx_value, dict_get_encoding (dict), print);
-               ds_put_cstr (&line_buffer, s);
-               free (s);
-              }
-
-           ds_put_char (&line_buffer, ' ');
-         }
+          for (column = 0; column < cmd.n_variables; column++)
+            {
+              const struct variable *v = cmd.v_variables[column];
+              const struct fmt_spec *print = var_get_print_format (v);
+              int width;
+              char *s;
+
+              if (target->type == 0 && column >= target->n_vertical)
+                {
+                  int name_len = strlen (var_get_name (v));
+                  width = MAX (name_len, print->w);
+                }
+              else
+                width = print->w;
+
+              if (width + ds_length(&line_buffer) > max_width &&
+                  ds_length(&line_buffer) != 0)
+                {
+                  if (!n_lines_remaining (d))
+                    {
+                      outp_eject_page (d);
+                      write_header (target);
+                    }
+
+                  write_line (d, ds_cstr (&line_buffer));
+                  ds_clear(&line_buffer);
+
+                  if (!target->header_rows)
+                    ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
+                }
+
+              if (width > print->w)
+                ds_put_char_multiple(&line_buffer, ' ', width - print->w);
+
+              if (fmt_is_string (print->type) || dict_contains_var (dict, v))
+                s = data_out (case_data (c, v), encoding, print);
+              else
+                {
+                  union value case_idx_value;
+                  case_idx_value.f = case_idx;
+                  s = data_out (&case_idx_value, encoding, print);
+                }
+
+              ds_put_cstr (&line_buffer, s);
+              free (s);
+              ds_put_char(&line_buffer, ' ');
+            }
 
-       if (!n_lines_remaining (d))
-         {
-           outp_eject_page (d);
-           write_header (d);
-         }
+          if (!n_lines_remaining (d))
+            {
+              outp_eject_page (d);
+              write_header (target);
+            }
 
-       write_line (d, ds_cstr (&line_buffer));
-       ds_clear(&line_buffer);
-      }
-    else if (d->class == &html_class)
-      {
-       struct html_driver_ext *x = d->ext;
-       int column;
+          write_line (d, ds_cstr (&line_buffer));
+          ds_clear(&line_buffer);
+        }
+      else if (d->class == &html_class)
+        {
+          struct html_driver_ext *x = d->ext;
+          int column;
 
-       fputs ("  <TR>\n", x->file);
+          fputs ("  <TR>\n", x->file);
 
-       for (column = 0; column < cmd.n_variables; column++)
-         {
-           const struct variable *v = cmd.v_variables[column];
-            const struct fmt_spec *print = var_get_print_format (v);
-           char *s = NULL;
-
-            if (fmt_is_string (print->type)
-                || dict_contains_var (dict, v))
-             s = data_out (case_data (c, v), dict_get_encoding (dict), print);
-            else
-              {
-                union value case_idx_value;
-                case_idx_value.f = case_idx;
-                s = data_out (&case_idx_value, dict_get_encoding (dict), print);
-              }
-
-            fputs ("    <TD>", x->file);
-            html_put_cell_contents (d, TAB_FIX, ss_buffer (s, print->w));
-           free (s);
-            fputs ("</TD>\n", x->file);
-         }
+          for (column = 0; column < cmd.n_variables; column++)
+            {
+              const struct variable *v = cmd.v_variables[column];
+              const struct fmt_spec *print = var_get_print_format (v);
+              char *s;
+
+              if (fmt_is_string (print->type)
+                  || dict_contains_var (dict, v))
+                s = data_out (case_data (c, v), encoding, print);
+              else
+                {
+                  union value case_idx_value;
+                  case_idx_value.f = case_idx;
+                  s = data_out (&case_idx_value, encoding, print);
+                }
+
+              fputs ("    <TD>", x->file);
+              html_put_cell_contents (d, TAB_FIX, ss_cstr (s));
+              fputs ("</TD>\n", x->file);
+
+              free (s);
+            }
 
-       fputs ("  </TR>\n", x->file);
-      }
-    else
-      NOT_REACHED ();
+          fputs ("  </TR>\n", x->file);
+        }
+      else
+        NOT_REACHED ();
+    }
 }
 
+
 /*
    Local Variables:
    mode: c
index 2cfa0177c77950f26495aa9a5bd421ab8df73488..9c8f56e84289196182fc10bb8599e26a35c8f3b3 100644 (file)
@@ -396,8 +396,8 @@ dump_table (struct print_trns *trns, const struct file_handle *fh)
   int row;
 
   spec_cnt = ll_count (&trns->specs);
-  t = tab_create (4, spec_cnt + 1, 0);
-  tab_columns (t, TAB_COL_DOWN, 1);
+  t = tab_create (4, spec_cnt + 1);
+  tab_columns (t, TAB_COL_DOWN);
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 3, spec_cnt);
   tab_hline (t, TAL_2, 0, 3, 1);
   tab_headers (t, 0, 0, 1, 0);
@@ -405,7 +405,7 @@ dump_table (struct print_trns *trns, const struct file_handle *fh)
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Record"));
   tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Columns"));
   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Format"));
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   row = 1;
   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
     {
index 5d2b42d758eed2d1e305d05046d59cf0aa290877..d2f59ccc813fe8661435bb03bee0cbf1a01caa20 100644 (file)
@@ -77,8 +77,8 @@ output_split_file_values (const struct dataset *ds, const struct ccase *c)
   if (split_cnt == 0)
     return;
 
-  t = tab_create (3, split_cnt + 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  t = tab_create (3, split_cnt + 1);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_vline (t, TAL_GAP, 1, 0, split_cnt);
   tab_vline (t, TAL_GAP, 2, 0, split_cnt);
   tab_text (t, 0, 0, TAB_NONE, _("Variable"));
@@ -95,6 +95,7 @@ output_split_file_values (const struct dataset *ds, const struct ccase *c)
       tab_text_format (t, 0, i + 1, TAB_LEFT, "%s", var_get_name (v));
 
       s = data_out (case_data (c, v), dict_get_encoding (dict), print);
+
       tab_text_format (t, 1, i + 1, 0, "%.*s", print->w, s);
 
       free (s);
index f68a830b83810ba6eec7d4e73b42ab40b041ca91..6c44a0c3baaefefbda7129b081442468106d7c9c 100644 (file)
@@ -69,17 +69,18 @@ static int describe_variable (const struct variable *v, struct tab_table *t,
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-sysfile_info_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+sysfile_info_dim (struct tab_rendering *r, void *aux UNUSED)
 {
+  const struct tab_table *t = r->table;
   static const int max[] = {20, 5, 35, 3, 0};
   const int *p;
   int i;
 
   for (p = max; *p; p++)
-    t->w[p - max] = MIN (tab_natural_width (t, d, p - max),
-                        *p * d->prop_em_width);
-  for (i = 0; i < t->nr; i++)
-    t->h[i] = tab_natural_height (t, d, i);
+    r->w[p - max] = MIN (tab_natural_width (r, p - max),
+                         *p * r->driver->prop_em_width);
+  for (i = 0; i < tab_nr (t); i++)
+    r->h[i] = tab_natural_height (r, i);
 }
 
 /* SYSFILE INFO utility. */
@@ -108,7 +109,7 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
     }
   casereader_destroy (reader);
 
-  t = tab_create (2, 11, 0);
+  t = tab_create (2, 11);
   tab_vline (t, TAL_GAP, 1, 0, 8);
   tab_text (t, 0, 0, TAB_LEFT, _("File:"));
   tab_text (t, 1, 0, TAB_LEFT, fh_get_file_name (h));
@@ -160,11 +161,11 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
                    dict_get_encoding(d) ? dict_get_encoding(d) : _("Unknown"));
 
 
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_submit (t);
 
-  t = tab_create (4, 1 + 2 * dict_get_var_cnt (d), 1);
-  tab_dim (t, sysfile_info_dim, NULL);
+  t = tab_create (4, 1 + 2 * dict_get_var_cnt (d));
+  tab_dim (t, sysfile_info_dim, NULL, NULL);
   tab_headers (t, 0, 0, 1, 0);
   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
   tab_joint_text (t, 1, 0, 2, 0, TAB_LEFT | TAT_TITLE, _("Description"));
@@ -339,44 +340,45 @@ display_documents (const struct dictionary *dict)
     }
 }
 
-static int _flags;
+struct variables_dim_aux
+  {
+    int flags;
+  };
 
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-variables_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+variables_dim (struct tab_rendering *r, void *aux_)
 {
-  int pc;
-  int i;
+  const struct outp_driver *d = r->driver;
+  struct variables_dim_aux *aux = aux_;
 
-  t->w[0] = tab_natural_width (t, d, 0);
-  if (_flags & (DF_VALUE_LABELS | DF_VARIABLE_LABELS | DF_MISSING_VALUES
-                | DF_AT_ATTRIBUTES | DF_ATTRIBUTES))
+  tab_natural_dimensions (r, NULL);
+  if (aux->flags & (DF_VALUE_LABELS | DF_VARIABLE_LABELS | DF_MISSING_VALUES
+                    | DF_AT_ATTRIBUTES | DF_ATTRIBUTES))
     {
-      t->w[1] = MAX (tab_natural_width (t, d, 1), d->prop_em_width * 5);
-      t->w[2] = MAX (tab_natural_width (t, d, 2), d->prop_em_width * 35);
-      pc = 3;
+      r->w[1] = MAX (r->w[1], d->prop_em_width * 5);
+      r->w[2] = MAX (r->w[2], d->prop_em_width * 35);
     }
-  else
-    pc = 1;
-  if (_flags & DF_DICT_INDEX)
-    t->w[pc] = tab_natural_width (t, d, pc);
+}
 
-  for (i = 0; i < t->nr; i++)
-    t->h[i] = tab_natural_height (t, d, i);
+static void
+variables_dim_free (void *aux_)
+{
+  struct variables_dim_aux *aux = aux_;
+  free (aux);
 }
 
 static void
 display_variables (const struct variable **vl, size_t n, int flags)
 {
   struct tab_table *t;
+  struct variables_dim_aux *aux;
   int nc;                      /* Number of columns. */
   int pc;                      /* `Position column' */
   int r;                       /* Current row. */
   size_t i;
 
-  _flags = flags;
-
   /* One column for the name,
      two columns for general description,
      one column for dictionary index. */
@@ -387,7 +389,7 @@ display_variables (const struct variable **vl, size_t n, int flags)
   if (flags & DF_DICT_INDEX)
     nc++;
 
-  t = tab_create (nc, n + 5, 1);
+  t = tab_create (nc, n + 5);
   tab_headers (t, 0, 0, 1, 0);
   tab_hline (t, TAL_2, 0, nc - 1, 1);
   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
@@ -397,7 +399,10 @@ display_variables (const struct variable **vl, size_t n, int flags)
                      ? _("Description") : _("Label")));
   if (flags & DF_DICT_INDEX)
     tab_text (t, pc, 0, TAB_LEFT | TAT_TITLE, _("Position"));
-  tab_dim (t, variables_dim, NULL);
+
+  aux = xmalloc (sizeof *aux);
+  aux->flags = flags;
+  tab_dim (t, variables_dim, variables_dim_free, aux);
 
   r = 1;
   for (i = 0; i < n; i++)
@@ -413,7 +418,7 @@ display_variables (const struct variable **vl, size_t n, int flags)
   if (flags & ~DF_DICT_INDEX)
     tab_vline (t, TAL_1, nc - 1, 0, r - 1);
   tab_resize (t, -1, r);
-  tab_columns (t, TAB_COL_DOWN, 1);
+  tab_columns (t, TAB_COL_DOWN);
   tab_submit (t);
 }
 \f
@@ -478,15 +483,15 @@ display_data_file_attributes (struct attrset *set, int flags)
   if (!n_attrs)
     return;
 
-  t = tab_create (2, n_attrs + 1, 0);
+  t = tab_create (2, n_attrs + 1);
   tab_headers (t, 0, 0, 1, 0);
   tab_box (t, TAL_1, TAL_1, -1, TAL_1, 0, 0, tab_nc (t) - 1, tab_nr (t) - 1);
   tab_hline (t, TAL_2, 0, 1, 1); 
   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Attribute"));
   tab_text (t, 1, 0, TAB_LEFT | TAT_TITLE, _("Value"));
   display_attributes (t, set, flags, 0, 1);
-  tab_columns (t, TAB_COL_DOWN, 1);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_columns (t, TAB_COL_DOWN);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_title (t, "Custom data file attributes.");
   tab_submit (t);
 }
@@ -713,10 +718,10 @@ display_vectors (const struct dictionary *dict, int sorted)
   if (sorted)
     qsort (vl, nvec, sizeof *vl, compare_vector_ptrs_by_name);
 
-  t = tab_create (4, nrow + 1, 0);
+  t = tab_create (4, nrow + 1);
   tab_headers (t, 0, 0, 1, 0);
-  tab_columns (t, TAB_COL_DOWN, 1);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_columns (t, TAB_COL_DOWN);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, 3, nrow);
   tab_box (t, -1, -1, -1, TAL_1, 0, 0, 3, nrow);
   tab_hline (t, TAL_2, 0, 3, 1);
index 1194110efa3ac850ed1584451e2782ec12870b83..8a39dbf0b2d2d6b6684766d8352d4cbf02cfdd59 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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
@@ -757,7 +757,7 @@ array_var_set_get_var (const struct var_set *vs, size_t idx)
 {
   struct array_var_set *avs = vs->aux;
 
-  return (struct variable *) avs->var[idx];
+  return CONST_CAST (struct variable *, avs->var[idx]);
 }
 
 /* If VS contains a variable named NAME, sets *IDX to its index
index 08d2f5e15c7d06176f1787b5e81017c7da54ad83..891e0ccaa3cb8f7c4bb4ba0da73feb1d456a1843 100644 (file)
@@ -965,20 +965,20 @@ dump_aggregate_info (struct agr_proc *agr, struct casewriter *output)
          case MEDIAN:
            {
              struct casereader *sorted_reader;
-             struct order_stats *median = percentile_create (0.5, i->cc);
+             struct percentile *median = percentile_create (0.5, i->cc);
+              struct order_stats *os = &median->parent;
 
              sorted_reader = casewriter_make_reader (i->writer);
 
-             order_stats_accumulate (&median, 1,
+             order_stats_accumulate (&os, 1,
                                      sorted_reader,
                                      i->weight,
                                      i->subject,
                                      i->exclude);
 
-             v->f = percentile_calculate ((struct percentile *) median,
-                                          PC_HAVERAGE);
+             v->f = percentile_calculate (median, PC_HAVERAGE);
 
-             statistic_destroy ((struct statistic *) median);
+             statistic_destroy (&median->parent.parent);
            }
            break;
          case SD:
index 26e0257ca14030ca3d58a2ae10acd8f960b45d2c..bc87a94da3316b0c0cf0b99c0035092a7d93a8cb 100644 (file)
@@ -187,16 +187,16 @@ binomial_execute (const struct dataset *ds,
       const struct fmt_spec *wfmt = wvar ?
        var_get_print_format (wvar) : & F_8_0;
 
-      struct tab_table *table = tab_create (7, ost->n_vars * 3 + 1, 0);
+      struct tab_table *table = tab_create (7, ost->n_vars * 3 + 1);
 
-      tab_dim (table, tab_natural_dimensions, NULL);
+      tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
       tab_title (table, _("Binomial Test"));
 
       tab_headers (table, 2, 0, 1, 0);
 
       tab_box (table, TAL_1, TAL_1, -1, TAL_1,
-               0, 0, table->nc - 1, tab_nr(table) - 1 );
+               0, 0, tab_nc (table) - 1, tab_nr(table) - 1 );
 
       for (v = 0 ; v < ost->n_vars; ++v)
         {
index 4593df4116eda81be5230a64ee2eee6c5f9fa15f..bc1b6474ff16c167d06ea94ca55de828475d7a9e 100644 (file)
@@ -31,6 +31,7 @@
 #include <language/stats/freq.h>
 #include <language/stats/npar.h>
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <libpspp/compiler.h>
 #include <libpspp/hash.h>
 #include <libpspp/message.h>
@@ -179,8 +180,8 @@ create_variable_frequency_table (const struct dictionary *dict,
       return NULL;
     }
 
-  table = tab_create(4, n_cells + 2, 0);
-  tab_dim (table, tab_natural_dimensions, NULL);
+  table = tab_create(4, n_cells + 2);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, var_to_string(var));
   tab_text (table, 1, 0, TAB_LEFT, _("Observed N"));
@@ -190,7 +191,7 @@ create_variable_frequency_table (const struct dictionary *dict,
   tab_headers (table, 1, 0, 1, 0);
 
   tab_box (table, TAL_1, TAL_1, -1, -1,
-          0, 0, table->nc - 1, tab_nr(table) - 1 );
+          0, 0, tab_nc (table) - 1, tab_nr(table) - 1 );
 
   tab_hline (table, TAL_1, 0, tab_nc(table) - 1, 1);
 
@@ -199,7 +200,7 @@ create_variable_frequency_table (const struct dictionary *dict,
     tab_vline (table, TAL_1, i, 0, tab_nr(table) - 1);
 
 
-  tab_text (table, 0, table->nr - 1, TAB_LEFT, _("Total"));
+  tab_text (table, 0, tab_nr (table) - 1, TAB_LEFT, _("Total"));
 
   return table;
 }
@@ -215,8 +216,8 @@ create_combo_frequency_table (const struct chisquare_test *test)
 
   int n_cells = test->hi - test->lo + 1;
 
-  table = tab_create(1 + ost->n_vars * 4, n_cells + 3, 0);
-  tab_dim (table, tab_natural_dimensions, NULL);
+  table = tab_create(1 + ost->n_vars * 4, n_cells + 3);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, _("Frequencies"));
   for ( i = 0 ; i < ost->n_vars ; ++i )
@@ -254,12 +255,12 @@ create_combo_frequency_table (const struct chisquare_test *test)
   tab_headers (table, 1, 0, 2, 0);
 
   tab_box (table, TAL_1, TAL_1, -1, -1,
-          0, 0, table->nc - 1, tab_nr(table) - 1 );
+          0, 0, tab_nc (table) - 1, tab_nr(table) - 1 );
 
   tab_hline (table, TAL_1, 1, tab_nc(table) - 1, 1);
   tab_hline (table, TAL_1, 0, tab_nc(table) - 1, 2);
 
-  tab_text (table, 0, table->nr - 1, TAB_LEFT, _("Total"));
+  tab_text (table, 0, tab_nr (table) - 1, TAB_LEFT, _("Total"));
 
   return table;
 }
@@ -271,8 +272,8 @@ create_stats_table (const struct chisquare_test *test)
   const struct one_sample_test *ost = (const struct one_sample_test*) test;
 
   struct tab_table *table;
-  table = tab_create (1 + ost->n_vars, 4, 0);
-  tab_dim (table, tab_natural_dimensions, NULL);
+  table = tab_create (1 + ost->n_vars, 4);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
   tab_title (table, _("Test Statistics"));
   tab_headers (table, 1, 0, 1, 0);
 
@@ -305,8 +306,9 @@ chisquare_execute (const struct dataset *ds,
 {
   const struct dictionary *dict = dataset_dict (ds);
   int v, i;
-  struct one_sample_test *ost = (struct one_sample_test *) test;
-  struct chisquare_test *cst = (struct chisquare_test *) test;
+  struct chisquare_test *cst = UP_CAST (test, struct chisquare_test,
+                                        parent.parent);
+  struct one_sample_test *ost = &cst->parent;
   int n_cells = 0;
   double total_expected = 0.0;
   const struct variable *wvar = dict_get_weight (dict);
index e397dae53b2ff482bb0ea9b31cc4b2685ddd14dd..ad90ef409051f47d12d8fcc2e48f5280fadf75aa 100644 (file)
@@ -108,9 +108,9 @@ output_descriptives (const struct corr *corr, const gsl_matrix *means,
   const int heading_columns = 1;
   const int heading_rows = 1;
 
-  struct tab_table *t = tab_create (nc, nr, 0);
+  struct tab_table *t = tab_create (nc, nr);
   tab_title (t, _("Descriptive Statistics"));
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   tab_headers (t, heading_columns, 0, heading_rows, 0);
 
@@ -203,9 +203,9 @@ output_correlation (const struct corr *corr, const struct corr_opts *opts,
   /* One header row */
   nr += heading_rows;
 
-  t = tab_create (nc, nr, 0);
+  t = tab_create (nc, nr);
   tab_title (t, _("Correlations"));
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   tab_headers (t, heading_columns, 0, heading_rows, 0);
 
index 99fa41d9c056a039ef9d92fdc9090758539c9d15..5695ed6478b64490095ea0c8abde83873fb38030 100644 (file)
@@ -201,6 +201,12 @@ struct crosstabs_proc
     unsigned int statistics;    /* Bit k is 1 if statistic k is requested. */
   };
 
+/* Auxiliary data structure for tab_dim. */
+struct crosstabs_dim_aux
+  {
+    enum mv_class exclude;
+  };
+
 static void
 init_proc (struct crosstabs_proc *proc, struct dataset *ds)
 {
@@ -230,8 +236,8 @@ free_proc (struct crosstabs_proc *proc)
 
          The rest of the data was allocated and destroyed at a
          lower level already. */
-      free (pt);
     }
+  free (proc->pivots);
 }
 
 static int internal_cmd_crosstabs (struct lexer *lexer, struct dataset *ds,
@@ -836,7 +842,7 @@ make_summary_table (struct crosstabs_proc *proc)
   struct string name;
   int i;
 
-  summary = tab_create (7, 3 + proc->n_pivots, 1);
+  summary = tab_create (7, 3 + proc->n_pivots);
   tab_title (summary, _("Summary."));
   tab_headers (summary, 1, 0, 3, 0);
   tab_joint_text (summary, 1, 0, 6, 0, TAB_CENTER, _("Cases"));
@@ -914,8 +920,8 @@ static void display_symmetric (struct crosstabs_proc *, struct pivot_table *,
 static void display_risk (struct pivot_table *, struct tab_table *);
 static void display_directional (struct crosstabs_proc *, struct pivot_table *,
                                  struct tab_table *);
-static void crosstabs_dim (struct tab_table *, struct outp_driver *,
-                           void *proc);
+static void crosstabs_dim (struct tab_rendering *, void *aux);
+static void crosstabs_dim_free (void *aux);
 static void table_value_missing (struct crosstabs_proc *proc,
                                  struct tab_table *table, int c, int r,
                                 unsigned char opt, const union value *v,
@@ -1147,8 +1153,7 @@ create_crosstab_table (struct crosstabs_proc *proc, struct pivot_table *pt)
   int i;
 
   table = tab_create (pt->n_consts + 1 + pt->n_cols + 1,
-                      (pt->n_entries / pt->n_cols) * 3 / 2 * proc->n_cells + 10,
-                      true);
+                      (pt->n_entries / pt->n_cols) * 3 / 2 * proc->n_cells + 10);
   tab_headers (table, pt->n_consts + 1, 0, 2, 0);
 
   /* First header line. */
@@ -1224,8 +1229,7 @@ create_chisq_table (struct pivot_table *pt)
   struct tab_table *chisq;
 
   chisq = tab_create (6 + (pt->n_vars - 2),
-                      pt->n_entries / pt->n_cols * 3 / 2 * N_CHISQ + 10,
-                      1);
+                      pt->n_entries / pt->n_cols * 3 / 2 * N_CHISQ + 10);
   tab_headers (chisq, 1 + (pt->n_vars - 2), 0, 1, 0);
 
   tab_title (chisq, _("Chi-square tests."));
@@ -1252,7 +1256,7 @@ create_sym_table (struct pivot_table *pt)
   struct tab_table *sym;
 
   sym = tab_create (6 + (pt->n_vars - 2),
-                    pt->n_entries / pt->n_cols * 7 + 10, 1);
+                    pt->n_entries / pt->n_cols * 7 + 10);
   tab_headers (sym, 2 + (pt->n_vars - 2), 0, 1, 0);
   tab_title (sym, _("Symmetric measures."));
 
@@ -1274,8 +1278,7 @@ create_risk_table (struct pivot_table *pt)
 {
   struct tab_table *risk;
 
-  risk = tab_create (4 + (pt->n_vars - 2), pt->n_entries / pt->n_cols * 4 + 10,
-                     1);
+  risk = tab_create (4 + (pt->n_vars - 2), pt->n_entries / pt->n_cols * 4 + 10);
   tab_headers (risk, 1 + pt->n_vars - 2, 0, 2, 0);
   tab_title (risk, _("Risk estimate."));
 
@@ -1300,7 +1303,7 @@ create_direct_table (struct pivot_table *pt)
   struct tab_table *direct;
 
   direct = tab_create (7 + (pt->n_vars - 2),
-                       pt->n_entries / pt->n_cols * 7 + 10, 1);
+                       pt->n_entries / pt->n_cols * 7 + 10);
   tab_headers (direct, 3 + (pt->n_vars - 2), 0, 1, 0);
   tab_title (direct, _("Directional measures."));
 
@@ -1348,6 +1351,7 @@ static void
 submit (struct crosstabs_proc *proc, struct pivot_table *pt,
         struct tab_table *t)
 {
+  struct crosstabs_dim_aux *aux;
   int i;
 
   if (t == NULL)
@@ -1370,33 +1374,39 @@ submit (struct crosstabs_proc *proc, struct pivot_table *pt,
   tab_box (t, -1, -1, -1, TAL_GAP, 0, tab_t (t), tab_l (t) - 1,
           tab_nr (t) - 1);
   tab_vline (t, TAL_2, tab_l (t), 0, tab_nr (t) - 1);
-  tab_dim (t, crosstabs_dim, proc);
+
+  aux = xmalloc (sizeof *aux);
+  aux->exclude = proc->exclude;
+  tab_dim (t, crosstabs_dim, crosstabs_dim_free, aux);
+
   tab_submit (t);
 }
 
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-crosstabs_dim (struct tab_table *t, struct outp_driver *d, void *proc_)
+crosstabs_dim (struct tab_rendering *r, void *aux_)
 {
-  struct crosstabs_proc *proc = proc_;
+  const struct tab_table *t = r->table;
+  struct outp_driver *d = r->driver;
+  struct crosstabs_dim_aux *aux = aux_;
   int i;
 
   /* Width of a numerical column. */
   int c = outp_string_width (d, "0.000000", OUTP_PROPORTIONAL);
-  if (proc->exclude == MV_NEVER)
+  if (aux->exclude == MV_NEVER)
     c += outp_string_width (d, "M", OUTP_PROPORTIONAL);
 
   /* Set width for header columns. */
-  if (t->l != 0)
+  if (tab_l (t) != 0)
     {
       size_t i;
       int w;
 
-      w = d->width - c * (t->nc - t->l);
-      for (i = 0; i <= t->nc; i++)
-        w -= t->wrv[i];
-      w /= t->l;
+      w = d->width - c * (tab_nc (t) - tab_l (t));
+      for (i = 0; i <= tab_nc (t); i++)
+        w -= r->wrv[i];
+      w /= tab_l (t);
 
       if (w < d->prop_em_width * 8)
        w = d->prop_em_width * 8;
@@ -1404,15 +1414,22 @@ crosstabs_dim (struct tab_table *t, struct outp_driver *d, void *proc_)
       if (w > d->prop_em_width * 15)
        w = d->prop_em_width * 15;
 
-      for (i = 0; i < t->l; i++)
-       t->w[i] = w;
+      for (i = 0; i < tab_l (t); i++)
+       r->w[i] = w;
     }
 
-  for (i = t->l; i < t->nc; i++)
-    t->w[i] = c;
+  for (i = tab_l (t); i < tab_nc (t); i++)
+    r->w[i] = c;
+
+  for (i = 0; i < tab_nr (t); i++)
+    r->h[i] = tab_natural_height (r, i);
+}
 
-  for (i = 0; i < t->nr; i++)
-    t->h[i] = tab_natural_height (t, d, i);
+static void
+crosstabs_dim_free (void *aux_)
+{
+  struct crosstabs_dim_aux *aux = aux_;
+  free (aux);
 }
 
 static bool
@@ -1513,26 +1530,21 @@ table_value_missing (struct crosstabs_proc *proc,
                      struct tab_table *table, int c, int r, unsigned char opt,
                     const union value *v, const struct variable *var)
 {
-  struct substring s;
-  const struct fmt_spec *print = var_get_print_format (var);
-
   const char *label = var_lookup_value_label (var, v);
-  if (label)
-    {
-      tab_text (table, c, r, TAB_LEFT, label);
-      return;
-    }
-
-  s = ss_cstr (data_out_pool (v, dict_get_encoding (proc->dict), print,
-                            table->container));
-  if (proc->exclude == MV_NEVER && var_is_num_missing (var, v->f, MV_USER))
-    s.string[s.length++] = 'M';
-  while (s.length && *s.string == ' ')
+  if (label != NULL)
+    tab_text (table, c, r, TAB_LEFT, label);
+  else
     {
-      s.length--;
-      s.string++;
+      const struct fmt_spec *print = var_get_print_format (var);
+      if (proc->exclude == MV_NEVER && var_is_value_missing (var, v, MV_USER))
+        {
+          char *s = data_out (v, dict_get_encoding (proc->dict), print);
+          tab_text_format (table, c, r, opt, "%sM", s + strspn (s, " "));
+          free (s);
+        }
+      else
+        tab_value (table, c, r, opt, v, proc->dict, print);
     }
-  tab_raw (table, c, r, opt, &s);
 }
 
 /* Draws a line across TABLE at the current row to indicate the most
@@ -1561,22 +1573,22 @@ format_cell_entry (struct tab_table *table, int c, int r, double value,
 {
   const struct fmt_spec f = {FMT_F, 10, 1};
   union value v;
-  struct substring s;
+  char suffixes[3];
+  int suffix_len;
+  char *s;
 
   v.f = value;
-  s = ss_cstr (data_out_pool (&v, dict_get_encoding (dict), &f, table->container));
+  s = data_out (&v, dict_get_encoding (dict), &f);
 
-  while (*s.string == ' ')
-    {
-      s.length--;
-      s.string++;
-    }
+  suffix_len = 0;
   if (suffix != 0)
-    s.string[s.length++] = suffix;
+    suffixes[suffix_len++] = suffix;
   if (mark_missing)
-    s.string[s.length++] = 'M';
+    suffixes[suffix_len++] = 'M';
+  suffixes[suffix_len] = '\0';
 
-  tab_raw (table, c, r, TAB_RIGHT, &s);
+  tab_text_format (table, c, r, TAB_RIGHT, "%s%s",
+                   s + strspn (s, " "), suffixes);
 }
 
 /* Displays the crosstabulation table. */
index 6b57fd297d8475ad63bcecc320a8c0c1b2d9201f..e78d771e147a95ea5630360d8e9c120984f96a14 100644 (file)
@@ -552,15 +552,15 @@ dump_z_table (struct dsc_proc *dsc)
        cnt++;
   }
 
-  t = tab_create (2, cnt + 1, 0);
+  t = tab_create (2, cnt + 1);
   tab_title (t, _("Mapping of variables to corresponding Z-scores."));
-  tab_columns (t, SOM_COL_DOWN, 1);
+  tab_columns (t, SOM_COL_DOWN);
   tab_headers (t, 0, 0, 1, 0);
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
   tab_hline (t, TAL_2, 0, 1, 1);
   tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   {
     size_t i, y;
@@ -873,13 +873,13 @@ display (struct dsc_proc *dsc)
     sort (dsc->vars, dsc->var_cnt, sizeof *dsc->vars,
           descriptives_compare_dsc_vars, dsc);
 
-  t = tab_create (nc, dsc->var_cnt + 1, 0);
+  t = tab_create (nc, dsc->var_cnt + 1);
   tab_headers (t, 1, 0, 1, 0);
   tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, dsc->var_cnt);
   tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
   tab_hline (t, TAL_2, 0, nc - 1, 1);
   tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   nc = 0;
   tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
index 08077942340234031939cded549e87886d5119ee..1d399b5a1888908a4b08c8cfe2874da0af039bed 100644 (file)
@@ -49,7 +49,7 @@
 #include <libpspp/str.h>
 #include <math/moments.h>
 #include <output/charts/box-whisker.h>
-#include <output/charts/cartesian.h>
+#include <output/charts/np-plot.h>
 #include <output/manager.h>
 #include <output/table.h>
 
@@ -104,11 +104,11 @@ struct factor_metrics
   struct percentile **ptl;
   size_t n_ptiles;
 
-  struct statistic *tukey_hinges;
-  struct statistic *box_whisker;
-  struct statistic *trimmed_mean;
-  struct statistic *histogram;
-  struct order_stats *np;
+  struct tukey_hinges *tukey_hinges;
+  struct box_whisker *box_whisker;
+  struct trimmed_mean *trimmed_mean;
+  struct histogram *histogram;
+  struct np *np;
 
   /* Three quartiles indexing into PTL */
   struct percentile **quartiles;
@@ -179,12 +179,12 @@ factor_destroy (struct xfactor *fctr)
          moments1_destroy (result->metrics[v].moments);
          extrema_destroy (result->metrics[v].minima);
          extrema_destroy (result->metrics[v].maxima);
-         statistic_destroy (result->metrics[v].trimmed_mean);
-         statistic_destroy (result->metrics[v].tukey_hinges);
-         statistic_destroy (result->metrics[v].box_whisker);
-         statistic_destroy (result->metrics[v].histogram);
+         statistic_destroy (&result->metrics[v].trimmed_mean->parent.parent);
+         statistic_destroy (&result->metrics[v].tukey_hinges->parent.parent);
+         statistic_destroy (&result->metrics[v].box_whisker->parent.parent);
+         statistic_destroy (&result->metrics[v].histogram->parent);
          for (i = 0 ; i < result->metrics[v].n_ptiles; ++i)
-           statistic_destroy ((struct statistic *) result->metrics[v].ptl[i]);
+           statistic_destroy (&result->metrics[v].ptl[i]->parent.parent);
          free (result->metrics[v].ptl);
          free (result->metrics[v].quartiles);
          casereader_destroy (result->metrics[v].up_reader);
@@ -320,82 +320,6 @@ cmd_examine (struct lexer *lexer, struct dataset *ds)
 };
 
 
-/* Plot the normal and detrended normal plots for RESULT.
-   Label the plots with LABEL */
-static void
-np_plot (struct np *np, const char *label)
-{
-  double yfirst = 0, ylast = 0;
-
-  double x_lower;
-  double x_upper;
-  double slack;
-
-  /* Normal Plot */
-  struct chart *np_chart;
-
-  /* Detrended Normal Plot */
-  struct chart *dnp_chart;
-
-  /* The slope and intercept of the ideal normal probability line */
-  const double slope = 1.0 / np->stddev;
-  const double intercept = -np->mean / np->stddev;
-
-  if ( np->n < 1.0 )
-    {
-      msg (MW, _("Not creating plot because data set is empty."));
-      return ;
-    }
-
-  np_chart = chart_create ();
-  dnp_chart = chart_create ();
-
-  if ( !np_chart || ! dnp_chart )
-    return ;
-
-  chart_write_title (np_chart, _("Normal Q-Q Plot of %s"), label);
-  chart_write_xlabel (np_chart, _("Observed Value"));
-  chart_write_ylabel (np_chart, _("Expected Normal"));
-
-  chart_write_title (dnp_chart, _("Detrended Normal Q-Q Plot of %s"),
-                    label);
-  chart_write_xlabel (dnp_chart, _("Observed Value"));
-  chart_write_ylabel (dnp_chart, _("Dev from Normal"));
-
-  yfirst = gsl_cdf_ugaussian_Pinv (1 / (np->n + 1));
-  ylast = gsl_cdf_ugaussian_Pinv (np->n / (np->n + 1));
-
-  /* Need to make sure that both the scatter plot and the ideal fit into the
-     plot */
-  x_lower = MIN (np->y_min, (yfirst - intercept) / slope) ;
-  x_upper = MAX (np->y_max, (ylast  - intercept) / slope) ;
-  slack = (x_upper - x_lower)  * 0.05 ;
-
-  chart_write_xscale (np_chart, x_lower - slack, x_upper + slack, 5);
-  chart_write_xscale (dnp_chart, np->y_min, np->y_max, 5);
-
-  chart_write_yscale (np_chart, yfirst, ylast, 5);
-  chart_write_yscale (dnp_chart, np->dns_min, np->dns_max, 5);
-
-  {
-    struct casereader *reader = casewriter_make_reader (np->writer);
-    struct ccase *c;
-    while ((c = casereader_read (reader)) != NULL)
-      {
-       chart_datum (np_chart, 0, case_data_idx (c, NP_IDX_Y)->f, case_data_idx (c, NP_IDX_NS)->f);
-       chart_datum (dnp_chart, 0, case_data_idx (c, NP_IDX_Y)->f, case_data_idx (c, NP_IDX_DNS)->f);
-
-       case_unref (c);
-      }
-    casereader_destroy (reader);
-  }
-
-  chart_line (dnp_chart, 0, 0, np->y_min, np->y_max , CHART_DIM_X);
-  chart_line (np_chart, slope, intercept, yfirst, ylast , CHART_DIM_Y);
-
-  chart_submit (np_chart);
-  chart_submit (dnp_chart);
-}
 
 
 static void
@@ -412,20 +336,37 @@ show_npplot (const struct variable **dependent_var,
           ll != ll_null (&fctr->result_list);
           ll = ll_next (ll))
        {
-         struct string str;
+         struct string label;
          const struct factor_result *result =
            ll_data (ll, struct factor_result, ll);
-
-         ds_init_empty (&str);
-         ds_put_format (&str, "%s ", var_get_name (dependent_var[v]));
-
-         factor_to_string (fctr, result, &str);
-
-         np_plot ((struct np*) result->metrics[v].np, ds_cstr(&str));
-
-         statistic_destroy ((struct statistic *)result->metrics[v].np);
-
-         ds_destroy (&str);
+          struct chart *npp, *dnpp;
+          struct casereader *reader;
+          struct np *np;
+
+         ds_init_empty (&label);
+         ds_put_format (&label, "%s ", var_get_name (dependent_var[v]));
+         factor_to_string (fctr, result, &label);
+
+          np = result->metrics[v].np;
+          reader = casewriter_make_reader (np->writer);
+          npp = np_plot_create (np, reader, ds_cstr (&label));
+          dnpp = dnp_plot_create (np, reader, ds_cstr (&label));
+
+         ds_destroy (&label);
+
+          if (npp == NULL || dnpp == NULL)
+            {
+              msg (MW, _("Not creating NP plot because data set is empty."));
+              chart_unref (npp);
+              chart_unref (dnpp);
+            }
+          else
+            {
+              chart_submit (npp);
+              chart_submit (dnpp);
+            }
+
+         statistic_destroy (&np->parent.parent);
        }
     }
 }
@@ -448,15 +389,25 @@ show_histogram (const struct variable **dependent_var,
          struct string str;
          const struct factor_result *result =
            ll_data (ll, struct factor_result, ll);
+          struct histogram *histogram;
+          double mean, var, n;
+
+          histogram = result->metrics[v].histogram;
+          if (histogram == NULL)
+            {
+              /* Probably all values are SYSMIS. */
+              continue;
+            }
 
          ds_init_empty (&str);
          ds_put_format (&str, "%s ", var_get_name (dependent_var[v]));
 
          factor_to_string (fctr, result, &str);
 
-         histogram_plot ((struct histogram *) result->metrics[v].histogram,
-                         ds_cstr (&str),
-                         (struct moments1 *) result->metrics[v].moments);
+          moments1_calculate (result->metrics[v].moments,
+                              &n, &mean, &var, NULL,  NULL);
+          chart_submit (histogram_chart_create (histogram, ds_cstr (&str),
+                                                n, mean, sqrt (var), false));
 
          ds_destroy (&str);
        }
@@ -474,25 +425,18 @@ show_boxplot_groups (const struct variable **dependent_var,
 
   for (v = 0; v < n_dep_var; ++v)
     {
-      struct ll *ll;
-      int f = 0;
-      struct chart *ch = chart_create ();
+      const struct factor_result *result;
+      struct boxplot *boxplot;
       double y_min = DBL_MAX;
       double y_max = -DBL_MAX;
+      char *title;
 
-      for (ll = ll_head (&fctr->result_list);
-          ll != ll_null (&fctr->result_list);
-          ll = ll_next (ll))
+      ll_for_each (result, struct factor_result, ll, &fctr->result_list)
        {
+          struct factor_metrics *metrics = &result->metrics[v];
+         const struct ll_list *max_list = extrema_list (metrics->maxima);
+         const struct ll_list *min_list = extrema_list (metrics->minima);
          const struct extremum  *max, *min;
-         const struct factor_result *result =
-           ll_data (ll, struct factor_result, ll);
-
-         const struct ll_list *max_list =
-           extrema_list (result->metrics[v].maxima);
-
-         const struct ll_list *min_list =
-           extrema_list (result->metrics[v].minima);
 
          if ( ll_is_empty (max_list))
            {
@@ -500,52 +444,34 @@ show_boxplot_groups (const struct variable **dependent_var,
              continue;
            }
 
-         max = (const struct extremum *)
-           ll_data (ll_head(max_list), struct extremum, ll);
-
-          min = (const struct extremum *)
-           ll_data (ll_head (min_list), struct extremum, ll);
+         max = ll_data (ll_head(max_list), struct extremum, ll);
+          min = ll_data (ll_head (min_list), struct extremum, ll);
 
          y_max = MAX (y_max, max->value);
          y_min = MIN (y_min, min->value);
        }
 
-      boxplot_draw_yscale (ch, y_max, y_min);
-
-      if ( fctr->indep_var[0])
-       chart_write_title (ch, _("Boxplot of %s vs. %s"),
+      if (fctr->indep_var[0])
+       title = xasprintf (_("Boxplot of %s vs. %s"),
                           var_to_string (dependent_var[v]),
-                          var_to_string (fctr->indep_var[0]) );
+                          var_to_string (fctr->indep_var[0]));
       else
-       chart_write_title (ch, _("Boxplot of %s"),
-                          var_to_string (dependent_var[v]));
+       title = xasprintf (_("Boxplot of %s"),
+                           var_to_string (dependent_var[v]));
+      boxplot = boxplot_create (y_min, y_max, title);
+      free (title);
 
-      for (ll = ll_head (&fctr->result_list);
-          ll != ll_null (&fctr->result_list);
-          ll = ll_next (ll))
+      ll_for_each (result, struct factor_result, ll, &fctr->result_list)
        {
-         const struct factor_result *result =
-           ll_data (ll, struct factor_result, ll);
-
-         struct string str;
-         const double box_width = (ch->data_right - ch->data_left)
-           / (ll_count (&fctr->result_list) * 2.0 ) ;
-
-         const double box_centre = (f++ * 2 + 1) * box_width + ch->data_left;
-
-         ds_init_empty (&str);
+          struct factor_metrics *metrics = &result->metrics[v];
+         struct string str = DS_EMPTY_INITIALIZER;
          factor_to_string_concise (fctr, result, &str);
-
-         boxplot_draw_boxplot (ch,
-                               box_centre, box_width,
-                               (const struct box_whisker *)
-                                result->metrics[v].box_whisker,
-                               ds_cstr (&str));
-
+          boxplot_add_box (boxplot, metrics->box_whisker, ds_cstr (&str));
+          metrics->box_whisker = NULL;
          ds_destroy (&str);
        }
 
-      chart_submit (ch);
+      chart_submit (boxplot_get_chart (boxplot));
     }
 }
 
@@ -558,74 +484,42 @@ show_boxplot_variables (const struct variable **dependent_var,
                        )
 
 {
+  const struct factor_result *result;
   int v;
-  struct ll *ll;
-  const struct ll_list *result_list = &fctr->result_list;
-
-  for (ll = ll_head (result_list);
-       ll != ll_null (result_list);
-       ll = ll_next (ll))
 
+  ll_for_each (result, struct factor_result, ll, &fctr->result_list)
     {
       struct string title;
-      struct chart *ch = chart_create ();
       double y_min = DBL_MAX;
       double y_max = -DBL_MAX;
-
-      const struct factor_result *result =
-       ll_data (ll, struct factor_result, ll);
-
-      const double box_width = (ch->data_right - ch->data_left)
-       / (n_dep_var * 2.0 ) ;
+      struct boxplot *boxplot;
 
       for (v = 0; v < n_dep_var; ++v)
        {
-         const struct ll *max_ll =
-           ll_head (extrema_list (result->metrics[v].maxima));
-         const struct ll *min_ll =
-           ll_head (extrema_list (result->metrics[v].minima));
-
-         const struct extremum  *max =
-           (const struct extremum *) ll_data (max_ll, struct extremum, ll);
-
-          const struct extremum  *min =
-           (const struct extremum *) ll_data (min_ll, struct extremum, ll);
+          const struct factor_metrics *metrics = &result->metrics[v];
+         const struct ll *max_ll = ll_head (extrema_list (metrics->maxima));
+         const struct ll *min_ll = ll_head (extrema_list (metrics->minima));
+         const struct extremum *max = ll_data (max_ll, struct extremum, ll);
+          const struct extremum *min = ll_data (min_ll, struct extremum, ll);
 
          y_max = MAX (y_max, max->value);
          y_min = MIN (y_min, min->value);
        }
 
-
-      boxplot_draw_yscale (ch, y_max, y_min);
-
       ds_init_empty (&title);
       factor_to_string (fctr, result, &title);
-
-#if 0
-      ds_put_format (&title, "%s = ", var_get_name (fctr->indep_var[0]));
-      var_append_value_name (fctr->indep_var[0], &result->value[0], &title);
-#endif
-
-      chart_write_title (ch, ds_cstr (&title));
+      boxplot = boxplot_create (y_min, y_max, ds_cstr (&title));
       ds_destroy (&title);
 
       for (v = 0; v < n_dep_var; ++v)
        {
-         struct string str;
-         const double box_centre = (v * 2 + 1) * box_width + ch->data_left;
-
-         ds_init_empty (&str);
-         ds_init_cstr (&str, var_get_name (dependent_var[v]));
-
-         boxplot_draw_boxplot (ch,
-                               box_centre, box_width,
-                               (const struct box_whisker *) result->metrics[v].box_whisker,
-                               ds_cstr (&str));
-
-         ds_destroy (&str);
+          struct factor_metrics *metrics = &result->metrics[v];
+          boxplot_add_box (boxplot, metrics->box_whisker,
+                           var_get_name (dependent_var[v]));
+          metrics->box_whisker = NULL;
        }
 
-      chart_submit (ch);
+      chart_submit (boxplot_get_chart (boxplot));
     }
 }
 
@@ -674,16 +568,14 @@ output_examine (const struct dictionary *dict)
       if ( cmd.sbc_percentiles)
        show_percentiles (dependent_vars, n_dependent_vars, factor);
 
-      if (cmd.a_plot[XMN_PLT_BOXPLOT] &&
-         cmd.cmp == XMN_GROUPS)
-       show_boxplot_groups (dependent_vars, n_dependent_vars, factor);
-
-
-      if (cmd.a_plot[XMN_PLT_BOXPLOT] &&
-         cmd.cmp == XMN_VARIABLES)
-       show_boxplot_variables (dependent_vars, n_dependent_vars,
-                               factor);
-
+      if (cmd.a_plot[XMN_PLT_BOXPLOT])
+        {
+          if (cmd.cmp == XMN_GROUPS)
+            show_boxplot_groups (dependent_vars, n_dependent_vars, factor);
+          else if (cmd.cmp == XMN_VARIABLES)
+            show_boxplot_variables (dependent_vars, n_dependent_vars, factor);
+        }
+      
       if (cmd.a_plot[XMN_PLT_HISTOGRAM])
        show_histogram (dependent_vars, n_dependent_vars, factor);
 
@@ -989,15 +881,13 @@ examine_group (struct cmd_examine *cmd, struct casereader *reader, int level,
 
          metric->n_ptiles = percentile_list.n_data;
 
-         metric->ptl = xcalloc (metric->n_ptiles,
-                                sizeof (struct percentile *));
+         metric->ptl = xcalloc (metric->n_ptiles, sizeof *metric->ptl);
 
          metric->quartiles = xcalloc (3, sizeof (*metric->quartiles));
 
          for (i = 0 ; i < metric->n_ptiles; ++i)
            {
-             metric->ptl[i] = (struct percentile *)
-               percentile_create (percentile_list.data[i] / 100.0, metric->n_valid);
+             metric->ptl[i] = percentile_create (percentile_list.data[i] / 100.0, metric->n_valid);
 
              if ( percentile_list.data[i] == 25)
                metric->quartiles[0] = metric->ptl[i];
@@ -1018,18 +908,18 @@ examine_group (struct cmd_examine *cmd, struct casereader *reader, int level,
              n_os ++;
            }
 
-         os = xcalloc (sizeof (struct order_stats *), n_os);
+          os = xcalloc (n_os, sizeof *os);
 
          for (i = 0 ; i < metric->n_ptiles ; ++i )
            {
-             os[i] = (struct order_stats *) metric->ptl[i];
+             os[i] = &metric->ptl[i]->parent;
            }
 
-         os[i] = (struct order_stats *) metric->tukey_hinges;
-         os[i+1] = (struct order_stats *) metric->trimmed_mean;
+         os[i] = &metric->tukey_hinges->parent;
+         os[i+1] = &metric->trimmed_mean->parent;
 
          if (cmd->a_plot[XMN_PLT_NPPLOT])
-           os[i+2] = metric->np;
+           os[i+2] = &metric->np->parent;
 
          order_stats_accumulate (os, n_os,
                                  casereader_clone (metric->up_reader),
@@ -1080,7 +970,7 @@ examine_group (struct cmd_examine *cmd, struct casereader *reader, int level,
            {
              struct factor_metrics *metric = &result->metrics[v];
              if ( metric->histogram)
-               histogram_add ((struct histogram *) metric->histogram,
+               histogram_add (metric->histogram,
                               case_data (c, dependent_vars[v])->f, weight);
            }
          case_unref (c);
@@ -1096,13 +986,13 @@ examine_group (struct cmd_examine *cmd, struct casereader *reader, int level,
          struct factor_metrics *metric = &result->metrics[v];
           int n_vals = caseproto_get_n_widths (casereader_get_proto (
                                                  metric->up_reader));
+          struct order_stats *os;
 
          metric->box_whisker =
-           box_whisker_create ((struct tukey_hinges *) metric->tukey_hinges,
-                               cmd->v_id, n_vals - 1);
+           box_whisker_create ( metric->tukey_hinges, cmd->v_id, n_vals - 1);
 
-         order_stats_accumulate ((struct order_stats **) &metric->box_whisker,
-                                 1,
+          os = &metric->box_whisker->parent;
+         order_stats_accumulate ( &os, 1,
                                  casereader_clone (metric->up_reader),
                                  wv, dependent_vars[v], MV_ANY);
        }
@@ -1242,10 +1132,10 @@ show_summary (const struct variable **dependent_var, int n_dep_var,
 
   n_cols = heading_columns + 6;
 
-  tbl = tab_create (n_cols, n_rows, 0);
+  tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   /* Outline the box */
   tab_box (tbl,
@@ -1479,10 +1369,10 @@ show_descriptives (const struct variable **dependent_var,
 
   n_cols = heading_columns + 2;
 
-  tbl = tab_create (n_cols, n_rows, 0);
+  tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   /* Outline the box */
   tab_box (tbl,
@@ -1658,7 +1548,7 @@ show_descriptives (const struct variable **dependent_var,
          tab_double (tbl, n_cols - 2,
                     heading_rows + row_var_start + 3 + i * DESCRIPTIVE_ROWS,
                     TAB_CENTER,
-                    trimmed_mean_calculate ((struct trimmed_mean *) result->metrics[v].trimmed_mean),
+                    trimmed_mean_calculate (result->metrics[v].trimmed_mean),
                     NULL);
 
 
@@ -1791,10 +1681,10 @@ show_extremes (const struct variable **dependent_var,
 
   n_cols = heading_columns + 2;
 
-  tbl = tab_create (n_cols, n_rows, 0);
+  tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   /* Outline the box */
   tab_box (tbl,
@@ -1995,10 +1885,10 @@ show_percentiles (const struct variable **dependent_var,
 
   n_cols = heading_columns + n_percentiles;
 
-  tbl = tab_create (n_cols, n_rows, 0);
+  tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   /* Outline the box */
   tab_box (tbl,
@@ -2078,8 +1968,7 @@ show_percentiles (const struct variable **dependent_var,
 
          tab_vline (tbl, TAL_1, n_cols - n_percentiles -1, heading_rows, n_rows - 1);
 
-         tukey_hinges_calculate ((struct tukey_hinges *) result->metrics[v].tukey_hinges,
-                                 hinges);
+         tukey_hinges_calculate (result->metrics[v].tukey_hinges, hinges);
 
          for (j = 0; j < n_percentiles; ++j)
            {
index 5a704d0d019f1f0d723aac1c8b81b455f02c0f7a..e1d6fb0e3dfcaa95518dc2f60b346da92199887b 100644 (file)
@@ -612,13 +612,14 @@ postcalc (const struct dataset *ds)
 
          hist = freq_tab_to_hist (ft,v);
 
-         histogram_plot_n (hist, var_to_string(v),
+          chart_submit (histogram_chart_create (
+                          hist, var_to_string(v),
                          vf->tab.valid_cases,
                          d[frq_mean],
                          d[frq_stddev],
-                         normal);
+                         normal));
 
-         statistic_destroy ((struct statistic *)hist);
+         statistic_destroy (&hist->parent);
        }
 
       if ( chart == GFT_PIE)
@@ -1000,26 +1001,39 @@ compare_freq_alpha_d (const void *a_, const void *b_, const void *v_)
 \f
 /* Frequency table display. */
 
+struct full_dim_aux
+  {
+    bool show_labels;
+  };
+
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-full_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+full_dim (struct tab_rendering *r, void *aux_)
 {
-  int i = 0;
-  int columns = 5;
+  const struct outp_driver *d = r->driver;
+  const struct tab_table *t = r->table;
+  const struct full_dim_aux *aux = aux_;
+  int i;
 
-  if (cmd.labels == FRQ_LABELS)
+  for (i = 0; i < tab_nc (t); i++)
     {
-    t->w[0] = MIN (tab_natural_width (t, d, 0), d->prop_em_width * 15);
-      i = 1;
-      columns ++;
+      r->w[i] = tab_natural_width (r, i);
+      if (aux->show_labels && i == 0)
+        r->w[i] = MIN (r->w[i], d->prop_em_width * 15);
+      else
+        r->w[i] = MAX (r->w[i], d->prop_em_width * 8);
     }
 
-  for (;i < columns; i++)
-    t->w[i] = MAX (tab_natural_width (t, d, i), d->prop_em_width * 8);
+  for (i = 0; i < tab_nr (t); i++)
+    r->h[i] = d->font_height;
+}
 
-  for (i = 0; i < t->nr; i++)
-    t->h[i] = d->font_height;
+static void
+full_dim_free (void *aux_)
+{
+  struct full_dim_aux *aux = aux_;
+  free (aux);
 }
 
 /* Displays a full frequency table for variable V. */
@@ -1046,12 +1060,17 @@ dump_full (const struct variable *v, const struct variable *wv)
 
   const bool lab = (cmd.labels == FRQ_LABELS);
 
+  struct full_dim_aux *aux;
+
   vf = get_var_freqs (v);
   ft = &vf->tab;
   n_categories = ft->n_valid + ft->n_missing;
-  t = tab_create (5 + lab, n_categories + 2, 0);
+  t = tab_create (5 + lab, n_categories + 2);
   tab_headers (t, 0, 0, 1, 0);
-  tab_dim (t, full_dim, NULL);
+
+  aux = xmalloc (sizeof *aux);
+  aux->show_labels = lab;
+  tab_dim (t, full_dim, full_dim_free, aux);
 
   if (lab)
     tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Value Label"));
@@ -1121,20 +1140,26 @@ dump_full (const struct variable *v, const struct variable *wv)
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-condensed_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+condensed_dim (struct tab_rendering *r, void *aux UNUSED)
 {
-  int cum_w = MAX (outp_string_width (d, _("Cum"), OUTP_PROPORTIONAL),
-                  MAX (outp_string_width (d, _("Cum"), OUTP_PROPORTIONAL),
-                       outp_string_width (d, "000", OUTP_PROPORTIONAL)));
+  struct outp_driver *d = r->driver;
+  const struct tab_table *t = r->table;
+
+  int cum_width = outp_string_width (d, _("Cum"), OUTP_PROPORTIONAL);
+  int zeros_width = outp_string_width (d, "000", OUTP_PROPORTIONAL);
+  int max_width = MAX (cum_width, zeros_width);
 
   int i;
 
   for (i = 0; i < 2; i++)
-    t->w[i] = MAX (tab_natural_width (t, d, i), d->prop_em_width * 8);
+    {
+      r->w[i] = tab_natural_width (r, i);
+      r->w[i] = MAX (r->w[i], d->prop_em_width * 8);
+    }
   for (i = 2; i < 4; i++)
-    t->w[i] = cum_w;
-  for (i = 0; i < t->nr; i++)
-    t->h[i] = d->font_height;
+    r->w[i] = max_width;
+  for (i = 0; i < tab_nr (t); i++)
+    r->h[i] = d->font_height;
 }
 
 /* Display condensed frequency table for variable V. */
@@ -1153,7 +1178,7 @@ dump_condensed (const struct variable *v, const struct variable *wv)
   vf = get_var_freqs (v);
   ft = &vf->tab;
   n_categories = ft->n_valid + ft->n_missing;
-  t = tab_create (4, n_categories + 2, 0);
+  t = tab_create (4, n_categories + 2);
 
   tab_headers (t, 0, 0, 2, 0);
   tab_text (t, 0, 1, TAB_CENTER | TAT_TITLE, _("Value"));
@@ -1161,7 +1186,7 @@ dump_condensed (const struct variable *v, const struct variable *wv)
   tab_text (t, 2, 1, TAB_CENTER | TAT_TITLE, _("Pct"));
   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Cum"));
   tab_text (t, 3, 1, TAB_CENTER | TAT_TITLE, _("Pct"));
-  tab_dim (t, condensed_dim, NULL);
+  tab_dim (t, condensed_dim, NULL, NULL);
 
   r = 2;
   for (f = ft->valid; f < ft->missing; f++)
@@ -1191,7 +1216,7 @@ dump_condensed (const struct variable *v, const struct variable *wv)
           0, 0, 3, r - 1);
   tab_hline (t, TAL_2, 0, 3, 2);
   tab_title (t, "%s", var_to_string (v));
-  tab_columns (t, SOM_COL_DOWN, 1);
+  tab_columns (t, SOM_COL_DOWN);
   tab_submit (t);
 }
 \f
@@ -1359,8 +1384,8 @@ dump_statistics (const struct variable *v, bool show_varname,
     }
   calc_stats (v, stat_value);
 
-  t = tab_create (3, n_stats + n_percentiles + 2, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  t = tab_create (3, n_stats + n_percentiles + 2);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   tab_box (t, TAL_1, TAL_1, -1, -1 , 0 , 0 , 2, tab_nr(t) - 1) ;
 
@@ -1401,7 +1426,7 @@ dump_statistics (const struct variable *v, bool show_varname,
                  var_get_print_format (v));
     }
 
-  tab_columns (t, SOM_COL_DOWN, 1);
+  tab_columns (t, SOM_COL_DOWN);
   if (show_varname)
     tab_title (t, "%s", var_to_string (v));
   else
@@ -1420,7 +1445,7 @@ freq_tab_to_hist (const struct freq_tab *ft, const struct variable *var)
   double x_min = DBL_MAX;
   double x_max = -DBL_MAX;
 
-  struct statistic *hist;
+  struct histogram *hist;
   const double bins = 11;
 
   struct hsh_iterator hi;
@@ -1442,10 +1467,10 @@ freq_tab_to_hist (const struct freq_tab *ft, const struct variable *var)
   for( i = 0 ; i < ft->n_valid ; ++i )
     {
       frq = &ft->valid[i];
-      histogram_add ((struct histogram *)hist, frq->value.f, frq->count);
+      histogram_add (hist, frq->value.f, frq->count);
     }
 
-  return (struct histogram *)hist;
+  return hist;
 }
 
 
@@ -1477,7 +1502,7 @@ freq_tab_to_slice_array(const struct freq_tab *frq_tab,
 
       ds_init_empty (&slices[i].label);
       var_append_value_name (var, &frq->value, &slices[i].label);
-      slices[i].magnetude = frq->count;
+      slices[i].magnitude = frq->count;
     }
 
   return slices;
@@ -1494,14 +1519,12 @@ do_piechart(const struct variable *var, const struct freq_tab *frq_tab)
 
   slices = freq_tab_to_slice_array(frq_tab, var, &n_slices);
 
-  piechart_plot(var_to_string(var), slices, n_slices);
+  chart_submit (piechart_create (var_to_string(var), slices, n_slices));
 
   for (i = 0 ; i < n_slices ; ++i )
-    {
-      ds_destroy (&slices[i].label);
-    }
+    ds_destroy (&slices[i].label);
 
-  free(slices);
+  free (slices);
 }
 
 
index d626dffac5f72075a582c370cb5940ec3845e2d1..8e6f11a8efa3aeb20591c7bf3476b8096c00e292 100644 (file)
@@ -102,16 +102,16 @@ do_summary_box (const struct descriptives *desc,
   if ( desc ) columns += 5;
   if ( quartiles ) columns += 3;
 
-  table = tab_create (columns, 2 + n_vars, 0);
+  table = tab_create (columns, 2 + n_vars);
 
-  tab_dim (table, tab_natural_dimensions, NULL);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, _("Descriptive Statistics"));
 
   tab_headers (table, 1, 0, 1, 0);
 
   tab_box (table, TAL_1, TAL_1, -1, TAL_1,
-          0, 0, table->nc - 1, tab_nr(table) - 1 );
+          0, 0, tab_nc (table) - 1, tab_nr(table) - 1 );
 
   tab_hline (table, TAL_2, 0, tab_nc (table) -1, 2);
   tab_vline (table, TAL_2, 1, 0, tab_nr (table) - 1);
index bbccce671f075b161b1e95170d07197c6a3ac69d..8c6f98f82cf669275376313a03650b9ba5de0ff8 100644 (file)
@@ -32,6 +32,7 @@
 #include <language/stats/chisquare.h>
 #include <language/stats/wilcoxon.h>
 #include <language/stats/sign.h>
+#include <libpspp/cast.h>
 #include <libpspp/hash.h>
 #include <libpspp/pool.h>
 #include <libpspp/taint.h>
@@ -223,10 +224,11 @@ npar_custom_chisquare (struct lexer *lexer, struct dataset *ds,
   struct npar_specs *specs = aux;
 
   struct chisquare_test *cstp = pool_alloc(specs->pool, sizeof(*cstp));
-  struct one_sample_test *tp = (struct one_sample_test *) cstp;
+  struct one_sample_test *tp = &cstp->parent;
+  struct npar_test *nt = &tp->parent;
 
-  ((struct npar_test *)tp)->execute = chisquare_execute;
-  ((struct npar_test *)tp)->insert_variables = one_sample_insert_variables;
+  nt->execute = chisquare_execute;
+  nt->insert_variables = one_sample_insert_variables;
 
   if (!parse_variables_const_pool (lexer, specs->pool, dataset_dict (ds),
                                   &tp->vars, &tp->n_vars,
@@ -316,7 +318,7 @@ npar_custom_chisquare (struct lexer *lexer, struct dataset *ds,
                              specs->test,
                              sizeof(*specs->test) * specs->n_tests);
 
-  specs->test[specs->n_tests - 1] = (struct npar_test *) tp;
+  specs->test[specs->n_tests - 1] = nt;
 
   return 1;
 }
@@ -328,10 +330,11 @@ npar_custom_binomial (struct lexer *lexer, struct dataset *ds,
 {
   struct npar_specs *specs = aux;
   struct binomial_test *btp = pool_alloc(specs->pool, sizeof(*btp));
-  struct one_sample_test *tp = (struct one_sample_test *) btp;
+  struct one_sample_test *tp = &btp->parent;
+  struct npar_test *nt = &tp->parent;
 
-  ((struct npar_test *)tp)->execute = binomial_execute;
-  ((struct npar_test *)tp)->insert_variables = one_sample_insert_variables;
+  nt->execute = binomial_execute;
+  nt->insert_variables = one_sample_insert_variables;
 
   btp->category1 = btp->category2 = btp->cutpoint = SYSMIS;
 
@@ -381,7 +384,7 @@ npar_custom_binomial (struct lexer *lexer, struct dataset *ds,
                              specs->test,
                              sizeof(*specs->test) * specs->n_tests);
 
-  specs->test[specs->n_tests - 1] = (struct npar_test *) tp;
+  specs->test[specs->n_tests - 1] = nt;
 
   return 1;
 }
@@ -412,7 +415,7 @@ parse_two_sample_related_test (struct lexer *lexer,
   const struct variable **vlist2;
   size_t n_vlist2;
 
-  ((struct npar_test *)test_parameters)->insert_variables = two_sample_insert_variables;
+  test_parameters->parent.insert_variables = two_sample_insert_variables;
 
   if (!parse_variables_const_pool (lexer, pool,
                                   dict,
@@ -512,7 +515,8 @@ npar_custom_wilcoxon (struct lexer *lexer,
   struct npar_specs *specs = aux;
 
   struct two_sample_test *tp = pool_alloc (specs->pool, sizeof(*tp));
-  ((struct npar_test *)tp)->execute = wilcoxon_execute;
+  struct npar_test *nt = &tp->parent;
+  nt->execute = wilcoxon_execute;
 
   if (!parse_two_sample_related_test (lexer, dataset_dict (ds), cmd,
                                      tp, specs->pool) )
@@ -522,7 +526,7 @@ npar_custom_wilcoxon (struct lexer *lexer,
   specs->test = pool_realloc (specs->pool,
                              specs->test,
                              sizeof(*specs->test) * specs->n_tests);
-  specs->test[specs->n_tests - 1] = (struct npar_test *) tp;
+  specs->test[specs->n_tests - 1] = nt;
 
   return 1;
 }
@@ -535,7 +539,8 @@ npar_custom_mcnemar (struct lexer *lexer,
   struct npar_specs *specs = aux;
 
   struct two_sample_test *tp = pool_alloc(specs->pool, sizeof(*tp));
-  ((struct npar_test *)tp)->execute = NULL;
+  struct npar_test *nt = &tp->parent;
+  nt->execute = NULL;
 
 
   if (!parse_two_sample_related_test (lexer, dataset_dict (ds),
@@ -546,7 +551,7 @@ npar_custom_mcnemar (struct lexer *lexer,
   specs->test = pool_realloc (specs->pool,
                              specs->test,
                              sizeof(*specs->test) * specs->n_tests);
-  specs->test[specs->n_tests - 1] = (struct npar_test *) tp;
+  specs->test[specs->n_tests - 1] = nt;
 
   return 1;
 }
@@ -558,7 +563,9 @@ npar_custom_sign (struct lexer *lexer, struct dataset *ds,
   struct npar_specs *specs = aux;
 
   struct two_sample_test *tp = pool_alloc(specs->pool, sizeof(*tp));
-  ((struct npar_test *) tp)->execute = sign_execute;
+  struct npar_test *nt = &tp->parent;
+
+  nt->execute = sign_execute;
 
   if (!parse_two_sample_related_test (lexer, dataset_dict (ds), cmd,
                                      tp, specs->pool) )
@@ -568,7 +575,7 @@ npar_custom_sign (struct lexer *lexer, struct dataset *ds,
   specs->test = pool_realloc (specs->pool,
                              specs->test,
                              sizeof(*specs->test) * specs->n_tests);
-  specs->test[specs->n_tests - 1] = (struct npar_test *) tp;
+  specs->test[specs->n_tests - 1] = nt;
 
   return 1;
 }
@@ -579,7 +586,7 @@ one_sample_insert_variables (const struct npar_test *test,
                             struct const_hsh_table *var_hash)
 {
   int i;
-  struct one_sample_test *ost = (struct one_sample_test *) test;
+  struct one_sample_test *ost = UP_CAST (test, struct one_sample_test, parent);
 
   for ( i = 0 ; i < ost->n_vars ; ++i )
     const_hsh_insert (var_hash, ost->vars[i]);
index 0f6b20a1db04cdf9503e4a21cd67cb2f22ecf6bf..ddb84d896fa1ac6701f8f4634071525a363ad3f9 100644 (file)
@@ -259,9 +259,9 @@ show_anova_table (void)
   struct tab_table *t;
 
 
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
 
   tab_box (t,
@@ -370,9 +370,9 @@ show_descriptives (const struct dictionary *dict)
   for ( v = 0; v < n_vars; ++v )
     n_rows += group_proc_get (vars[v])->n_groups + 1;
 
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 2, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
 
   /* Put a frame around the entire box, and vertical lines inside */
@@ -517,9 +517,9 @@ show_homogeneity (void)
   struct tab_table *t;
 
 
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 1, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   /* Put a frame around the entire box, and vertical lines inside */
   tab_box (t,
@@ -577,9 +577,9 @@ show_contrast_coeffs (short *bad_contrast)
 
   struct tab_table *t;
 
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 2, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   /* Put a frame around the entire box, and vertical lines inside */
   tab_box (t,
@@ -661,9 +661,9 @@ show_contrast_tests (short *bad_contrast)
 
   struct tab_table *t;
 
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 3, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   /* Put a frame around the entire box, and vertical lines inside */
   tab_box (t,
index 41a23f3e2580ebd9a9aac6ff289b89803c6f8da2..b61ae586b488292d23fda2b8ee9b96daab4d37f2 100644 (file)
@@ -149,8 +149,8 @@ reg_stats_r (pspp_linreg_cache * c)
   rsq = c->ssm / c->sst;
   adjrsq = 1.0 - (1.0 - rsq) * (c->n_obs - 1.0) / (c->n_obs - c->n_indeps);
   std_error = sqrt (pspp_linreg_mse (c));
-  t = tab_create (n_cols, n_rows, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  t = tab_create (n_cols, n_rows);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
@@ -191,9 +191,9 @@ reg_stats_coeff (pspp_linreg_cache * c)
   assert (c != NULL);
   n_rows = c->n_coeffs + 3;
 
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
@@ -288,9 +288,9 @@ reg_stats_anova (pspp_linreg_cache * c)
   struct tab_table *t;
 
   assert (c != NULL);
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
 
@@ -379,9 +379,9 @@ reg_stats_bcov (pspp_linreg_cache * c)
   assert (c != NULL);
   n_cols = c->n_indeps + 1 + 2;
   n_rows = 2 * (c->n_indeps + 1);
-  t = tab_create (n_cols, n_rows, 0);
+  t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL);
+  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
index 681669d18999fdac3c5516c9815b9079aae99dfb..dfb81367912a3bcd96a25aa00bb964145a98f0ab 100644 (file)
@@ -381,9 +381,9 @@ run_reliability (struct casereader *input, struct dataset *ds,
 
 
   {
-    struct tab_table *tab = tab_create(1, 1, 0);
+    struct tab_table *tab = tab_create(1, 1);
 
-    tab_dim (tab, tab_natural_dimensions, NULL);
+    tab_dim (tab, tab_natural_dimensions, NULL, NULL);
     tab_flags (tab, SOMF_NO_TITLE );
 
     tab_text_format (tab, 0, 0, 0, "Scale: %s", ds_cstr (&rel->scale_name));
@@ -425,10 +425,10 @@ reliability_statistics (const struct reliability *rel)
   int heading_columns = rol[rel->model].heading_cols;
   int heading_rows = rol[rel->model].heading_rows;
 
-  struct tab_table *tbl = tab_create (n_cols, n_rows, 0);
+  struct tab_table *tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   tab_title (tbl, _("Reliability Statistics"));
 
@@ -468,10 +468,10 @@ reliability_summary_total (const struct reliability *rel)
   const int heading_rows = 1;
   const int n_rows = rel->sc[0].n_items + heading_rows ;
 
-  struct tab_table *tbl = tab_create (n_cols, n_rows, 0);
+  struct tab_table *tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   tab_title (tbl, _("Item-Total Statistics"));
 
@@ -678,10 +678,10 @@ case_processing_summary (casenumber n_valid, casenumber n_missing,
   int heading_columns = 2;
   int heading_rows = 1;
   struct tab_table *tbl;
-  tbl = tab_create (n_cols, n_rows, 0);
+  tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   tab_title (tbl, _("Case Processing Summary"));
 
index 1d61a55c57a3cad25c834c204c3571632d9df778..1d21a4f6a1221ed6b83f3c2bbb8d4e25c457c95a 100644 (file)
@@ -16,6 +16,8 @@
 
 #include <config.h>
 
+#include <language/stats/roc.h>
+
 #include <data/procedure.h>
 #include <language/lexer/variable-parser.h>
 #include <language/lexer/value-parser.h>
@@ -36,8 +38,8 @@
 #include <gsl/gsl_cdf.h>
 #include <output/table.h>
 
-#include <output/charts/plot-chart.h>
-#include <output/charts/cartesian.h>
+#include <output/chart.h>
+#include <output/charts/roc-chart.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -383,13 +385,6 @@ struct roc_state
   double max;
 };
 
-#define CUTPOINT 0
-#define TP 1
-#define FN 2
-#define TN 3
-#define FP 4
-
-
 /* 
    Return a new casereader based upon CUTPOINT_RDR.
    The number of "positive" cases are placed into
@@ -415,7 +410,7 @@ accumulate_counts (struct casereader *cutpoint_rdr,
   for ( ; (cpc = casereader_read (r) ); case_unref (cpc))
     {
       struct ccase *new_case;
-      const double cp = case_data_idx (cpc, CUTPOINT)->f;
+      const double cp = case_data_idx (cpc, ROC_CUTPOINT)->f;
 
       assert (cp != SYSMIS);
 
@@ -579,7 +574,7 @@ process_positive_group (const struct variable *var, struct casereader *reader,
   return process_group (var, reader, gt, dict, &rs->n1,
                        &rs->cutpoint_rdr,
                        ge,
-                       TP, FN);
+                       ROC_TP, ROC_FN);
 }
 
 /*
@@ -597,7 +592,7 @@ process_negative_group (const struct variable *var, struct casereader *reader,
   return process_group (var, reader, lt, dict, &rs->n2,
                        &rs->cutpoint_rdr,
                        lt,
-                       TN, FP);
+                       ROC_TN, ROC_FP);
 }
 
 
@@ -608,11 +603,11 @@ append_cutpoint (struct casewriter *writer, double cutpoint)
 {
   struct ccase *cc = case_create (casewriter_get_proto (writer));
 
-  case_data_rw_idx (cc, CUTPOINT)->f = cutpoint;
-  case_data_rw_idx (cc, TP)->f = 0;
-  case_data_rw_idx (cc, FN)->f = 0;
-  case_data_rw_idx (cc, TN)->f = 0;
-  case_data_rw_idx (cc, FP)->f = 0;
+  case_data_rw_idx (cc, ROC_CUTPOINT)->f = cutpoint;
+  case_data_rw_idx (cc, ROC_TP)->f = 0;
+  case_data_rw_idx (cc, ROC_FN)->f = 0;
+  case_data_rw_idx (cc, ROC_TN)->f = 0;
+  case_data_rw_idx (cc, ROC_FP)->f = 0;
 
   casewriter_write (writer, cc);
 }
@@ -620,9 +615,9 @@ append_cutpoint (struct casewriter *writer, double cutpoint)
 
 /* 
    Create and initialise the rs[x].cutpoint_rdr casereaders.  That is, the readers will
-   be created with width 5, ready to take the values (cutpoint, TP, FN, TN, FP), and the
+   be created with width 5, ready to take the values (cutpoint, ROC_TP, ROC_FN, ROC_TN, ROC_FP), and the
    reader will be populated with its final number of cases.
-   However on exit from this function, only CUTPOINT entries will be set to their final
+   However on exit from this function, only ROC_CUTPOINT entries will be set to their final
    value.  The other entries will be initialised to zero.
 */
 static void
@@ -634,13 +629,13 @@ prepare_cutpoints (struct cmd_roc *roc, struct roc_state *rs, struct casereader
   struct caseproto *proto = caseproto_create ();
 
   struct subcase ordering;
-  subcase_init (&ordering, CUTPOINT, 0, SC_ASCEND);
+  subcase_init (&ordering, ROC_CUTPOINT, 0, SC_ASCEND);
 
   proto = caseproto_add_width (proto, 0); /* cutpoint */
-  proto = caseproto_add_width (proto, 0); /* TP */
-  proto = caseproto_add_width (proto, 0); /* FN */
-  proto = caseproto_add_width (proto, 0); /* TN */
-  proto = caseproto_add_width (proto, 0); /* FP */
+  proto = caseproto_add_width (proto, 0); /* ROC_TP */
+  proto = caseproto_add_width (proto, 0); /* ROC_FN */
+  proto = caseproto_add_width (proto, 0); /* ROC_TN */
+  proto = caseproto_add_width (proto, 0); /* ROC_FP */
 
   for (i = 0 ; i < roc->n_vars; ++i)
     {
@@ -932,7 +927,7 @@ show_auc  (struct roc_state *rs, const struct cmd_roc *roc)
   const int n_fields = roc->print_se ? 5 : 1;
   const int n_cols = roc->n_vars > 1 ? n_fields + 1: n_fields;
   const int n_rows = 2 + roc->n_vars;
-  struct tab_table *tbl = tab_create (n_cols, n_rows, 0);
+  struct tab_table *tbl = tab_create (n_cols, n_rows);
 
   if ( roc->n_vars > 1)
     tab_title (tbl, _("Area Under the Curve"));
@@ -941,7 +936,7 @@ show_auc  (struct roc_state *rs, const struct cmd_roc *roc)
 
   tab_headers (tbl, n_cols - n_fields, 0, 1, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   tab_text (tbl, n_cols - n_fields, 1, TAT_TITLE, _("Area"));
 
@@ -1027,13 +1022,13 @@ show_summary (const struct cmd_roc *roc)
 {
   const int n_cols = 3;
   const int n_rows = 4;
-  struct tab_table *tbl = tab_create (n_cols, n_rows, 0);
+  struct tab_table *tbl = tab_create (n_cols, n_rows);
 
   tab_title (tbl, _("Case Summary"));
 
   tab_headers (tbl, 1, 0, 2, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   tab_box (tbl,
           TAL_2, TAL_2,
@@ -1085,7 +1080,7 @@ show_coords (struct roc_state *rs, const struct cmd_roc *roc)
   for (i = 0; i < roc->n_vars; ++i)
     n_rows += casereader_count_cases (rs[i].cutpoint_rdr);
 
-  tbl = tab_create (n_cols, n_rows, 0);
+  tbl = tab_create (n_cols, n_rows);
 
   if ( roc->n_vars > 1)
     tab_title (tbl, _("Coordinates of the Curve"));
@@ -1095,7 +1090,7 @@ show_coords (struct roc_state *rs, const struct cmd_roc *roc)
 
   tab_headers (tbl, 1, 0, 1, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL);
+  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   tab_hline (tbl, TAL_2, 0, n_cols - 1, 1);
 
@@ -1131,21 +1126,21 @@ show_coords (struct roc_state *rs, const struct cmd_roc *roc)
       for (; (cc = casereader_read (r)) != NULL;
           case_unref (cc), x++)
        {
-         const double se = case_data_idx (cc, TP)->f /
+         const double se = case_data_idx (cc, ROC_TP)->f /
            (
-            case_data_idx (cc, TP)->f
+            case_data_idx (cc, ROC_TP)->f
             +
-            case_data_idx (cc, FN)->f
+            case_data_idx (cc, ROC_FN)->f
             );
 
-         const double sp = case_data_idx (cc, TN)->f /
+         const double sp = case_data_idx (cc, ROC_TN)->f /
            (
-            case_data_idx (cc, TN)->f
+            case_data_idx (cc, ROC_TN)->f
             +
-            case_data_idx (cc, FP)->f
+            case_data_idx (cc, ROC_FP)->f
             );
 
-         tab_double (tbl, n_cols - 3, x, 0, case_data_idx (cc, CUTPOINT)->f,
+         tab_double (tbl, n_cols - 3, x, 0, case_data_idx (cc, ROC_CUTPOINT)->f,
                      var_get_print_format (roc->vars[i]));
 
          tab_double (tbl, n_cols - 2, x, 0, se, NULL);
@@ -1159,68 +1154,25 @@ show_coords (struct roc_state *rs, const struct cmd_roc *roc)
 }
 
 
-static void
-draw_roc (struct roc_state *rs, const struct cmd_roc *roc)
-{
-  int i;
-
-  struct chart *roc_chart = chart_create ();
-
-  chart_write_title (roc_chart, _("ROC Curve"));
-  chart_write_xlabel (roc_chart, _("1 - Specificity"));
-  chart_write_ylabel (roc_chart, _("Sensitivity"));
-
-  chart_write_xscale (roc_chart, 0, 1, 5);
-  chart_write_yscale (roc_chart, 0, 1, 5);
-
-  if ( roc->reference )
-    {
-      chart_line (roc_chart, 1.0, 0,
-                 0.0, 1.0,
-                 CHART_DIM_X);
-    }
-
-  for (i = 0; i < roc->n_vars; ++i)
-    {
-      struct ccase *cc;
-      struct casereader *r = casereader_clone (rs[i].cutpoint_rdr);
-
-      chart_vector_start (roc_chart, var_get_name (roc->vars[i]));
-      for (; (cc = casereader_read (r)) != NULL;
-          case_unref (cc))
-       {
-         double se = case_data_idx (cc, TP)->f;
-         double sp = case_data_idx (cc, TN)->f;
-
-         se /= case_data_idx (cc, FN)->f +
-           case_data_idx (cc, TP)->f ;
-
-         sp /= case_data_idx (cc, TN)->f +
-           case_data_idx (cc, FP)->f ;
-
-         chart_vector (roc_chart, 1 - sp, se);
-       }
-      chart_vector_end (roc_chart);
-      casereader_destroy (r);
-    }
-
-  chart_write_legend (roc_chart);
-
-  chart_submit (roc_chart);
-}
-
-
 static void
 output_roc (struct roc_state *rs, const struct cmd_roc *roc)
 {
   show_summary (roc);
 
   if ( roc->curve )
-    draw_roc (rs, roc);
+    {
+      struct roc_chart *rc;
+      size_t i;
+
+      rc = roc_chart_create (roc->reference);
+      for (i = 0; i < roc->n_vars; i++)
+        roc_chart_add_var (rc, var_get_name (roc->vars[i]),
+                           rs[i].cutpoint_rdr);
+      chart_submit (roc_chart_get_chart (rc));
+    }
 
   show_auc (rs, roc);
 
-
   if ( roc->print_coords )
     show_coords (rs, roc);
 }
diff --git a/src/language/stats/roc.h b/src/language/stats/roc.h
new file mode 100644 (file)
index 0000000..5d63c96
--- /dev/null
@@ -0,0 +1,28 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef LANGUAGE_STATS_ROC_H
+#define LANGUAGE_STATS_ROC_H 1
+
+/* These are case indexes into the cutpoint case readers for ROC
+   output, used by roc.c and roc-chart.c. */
+#define ROC_CUTPOINT 0
+#define ROC_TP 1
+#define ROC_FN 2
+#define ROC_TN 3
+#define ROC_FP 4
+
+#endif /* language/stats/roc.h */
index a5a272126007c557a2c006b652d73cf7e9fe4436..d5970c6ff0a61297803374c92755762cdca1f6d0 100644 (file)
@@ -52,12 +52,12 @@ output_frequency_table (const struct two_sample_test *t2s,
                        const struct dictionary *dict)
 {
   int i;
-  struct tab_table *table = tab_create (3, 1 + 4 * t2s->n_pairs, 0);
+  struct tab_table *table = tab_create (3, 1 + 4 * t2s->n_pairs);
 
   const struct variable *wv = dict_get_weight (dict);
   const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
 
-  tab_dim (table, tab_natural_dimensions, NULL);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, _("Frequencies"));
 
@@ -65,11 +65,11 @@ output_frequency_table (const struct two_sample_test *t2s,
 
   /* Vertical lines inside the box */
   tab_box (table, 0, 0, -1, TAL_1,
-          1, 0, table->nc - 1, tab_nr (table) - 1 );
+          1, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
 
   /* Box around entire table */
   tab_box (table, TAL_2, TAL_2, -1, -1,
-          0, 0, table->nc - 1, tab_nr (table) - 1 );
+          0, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
 
   tab_text (table,  2, 0,  TAB_CENTER, _("N"));
 
@@ -86,7 +86,7 @@ output_frequency_table (const struct two_sample_test *t2s,
 
       ds_destroy (&pair_name);
 
-      tab_hline (table, TAL_1, 0, table->nc - 1, 1 + i * 4);
+      tab_hline (table, TAL_1, 0, tab_nc (table) - 1, 1 + i * 4);
 
       tab_text (table,  1, 1 + i * 4,  TAB_LEFT, _("Negative Differences"));
       tab_text (table,  1, 2 + i * 4,  TAB_LEFT, _("Positive Differences"));
@@ -108,26 +108,26 @@ output_statistics_table (const struct two_sample_test *t2s,
                         const struct sign_test_params *param)
 {
   int i;
-  struct tab_table *table = tab_create (1 + t2s->n_pairs, 4, 0);
+  struct tab_table *table = tab_create (1 + t2s->n_pairs, 4);
 
-  tab_dim (table, tab_natural_dimensions, NULL);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, _("Test Statistics"));
 
   tab_headers (table, 0, 1,  0, 1);
 
-  tab_hline (table, TAL_2, 0, table->nc - 1, 1);
-  tab_vline (table, TAL_2, 1, 0, table->nr - 1);
+  tab_hline (table, TAL_2, 0, tab_nc (table) - 1, 1);
+  tab_vline (table, TAL_2, 1, 0, tab_nr (table) - 1);
 
 
   /* Vertical lines inside the box */
   tab_box (table, -1, -1, -1, TAL_1,
           0, 0,
-          table->nc - 1, tab_nr (table) - 1);
+          tab_nc (table) - 1, tab_nr (table) - 1);
 
   /* Box around entire table */
   tab_box (table, TAL_2, TAL_2, -1, -1,
-          0, 0, table->nc - 1,
+          0, 0, tab_nc (table) - 1,
           tab_nr (table) - 1);
 
   tab_text (table,  0, 1, TAT_TITLE | TAB_LEFT,
index c448d52ea035c3d163bef9bf343a8d6b9609f904..d417d31c0d26e944b9548756e05e2baf1ea178ad 100644 (file)
@@ -476,13 +476,13 @@ static void
 ssbox_base_init (struct ssbox *this, int cols, int rows)
 {
   this->finalize = ssbox_base_finalize;
-  this->t = tab_create (cols, rows, 0);
+  this->t = tab_create (cols, rows);
 
-  tab_columns (this->t, SOM_COL_DOWN, 1);
+  tab_columns (this->t, SOM_COL_DOWN);
   tab_headers (this->t, 0, 0, 1, 0);
   tab_box (this->t, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols - 1, rows - 1);
   tab_hline (this->t, TAL_2, 0, cols- 1, 1);
-  tab_dim (this->t, tab_natural_dimensions, NULL);
+  tab_dim (this->t, tab_natural_dimensions, NULL, NULL);
 }
 \f
 /* ssbox implementations. */
@@ -1068,11 +1068,11 @@ trbox_base_init (struct trbox *self, size_t data_rows, int cols)
   const size_t rows = 3 + data_rows;
 
   self->finalize = trbox_base_finalize;
-  self->t = tab_create (cols, rows, 0);
+  self->t = tab_create (cols, rows);
   tab_headers (self->t, 0, 0, 3, 0);
   tab_box (self->t, TAL_2, TAL_2, TAL_0, TAL_0, 0, 0, cols - 1, rows - 1);
   tab_hline (self->t, TAL_2, 0, cols- 1, 3);
-  tab_dim (self->t, tab_natural_dimensions, NULL);
+  tab_dim (self->t, tab_natural_dimensions, NULL, NULL);
 }
 
 /* Base finalizer for the trbox */
@@ -1092,14 +1092,14 @@ pscbox (struct t_test_proc *proc)
 
   struct tab_table *table;
 
-  table = tab_create (cols, rows, 0);
+  table = tab_create (cols, rows);
 
-  tab_columns (table, SOM_COL_DOWN, 1);
+  tab_columns (table, SOM_COL_DOWN);
   tab_headers (table, 0, 0, 1, 0);
   tab_box (table, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols - 1, rows - 1);
   tab_hline (table, TAL_2, 0, cols - 1, 1);
   tab_vline (table, TAL_2, 2, 0, rows - 1);
-  tab_dim (table, tab_natural_dimensions, NULL);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
   tab_title (table, _("Paired Samples Correlations"));
 
   /* column headings */
index 310206c0e7bf88961e820c05f97831f8568da5a6..c7e302779e63a8937c64a707eeb74126823e948c 100644 (file)
@@ -225,9 +225,9 @@ show_ranks_box (const struct wilcoxon_state *ws,
   const struct variable *wv = dict_get_weight (dict);
   const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
 
-  struct tab_table *table = tab_create (5, 1 + 4 * t2s->n_pairs, 0);
+  struct tab_table *table = tab_create (5, 1 + 4 * t2s->n_pairs);
 
-  tab_dim (table, tab_natural_dimensions, NULL);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, _("Ranks"));
 
@@ -235,11 +235,11 @@ show_ranks_box (const struct wilcoxon_state *ws,
 
   /* Vertical lines inside the box */
   tab_box (table, 0, 0, -1, TAL_1,
-          1, 0, table->nc - 1, tab_nr (table) - 1 );
+          1, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
 
   /* Box around entire table */
   tab_box (table, TAL_2, TAL_2, -1, -1,
-          0, 0, table->nc - 1, tab_nr (table) - 1 );
+          0, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
 
 
   tab_text (table,  2, 0,  TAB_CENTER, _("N"));
@@ -261,7 +261,7 @@ show_ranks_box (const struct wilcoxon_state *ws,
       tab_text (table, 1, 3 + i * 4, TAB_LEFT, _("Ties"));
       tab_text (table, 1, 4 + i * 4, TAB_LEFT, _("Total"));
 
-      tab_hline (table, TAL_1, 0, table->nc - 1, 1 + i * 4);
+      tab_hline (table, TAL_1, 0, tab_nc (table) - 1, 1 + i * 4);
 
 
       tab_text (table, 0, 1 + i * 4, TAB_LEFT, ds_cstr (&pair_name));
@@ -290,8 +290,8 @@ show_ranks_box (const struct wilcoxon_state *ws,
 
     }
 
-  tab_hline (table, TAL_2, 0, table->nc - 1, 1);
-  tab_vline (table, TAL_2, 2, 0, table->nr - 1);
+  tab_hline (table, TAL_2, 0, tab_nc (table) - 1, 1);
+  tab_vline (table, TAL_2, 2, 0, tab_nr (table) - 1);
 
 
   tab_submit (table);
@@ -306,9 +306,9 @@ show_tests_box (const struct wilcoxon_state *ws,
                )
 {
   size_t i;
-  struct tab_table *table = tab_create (1 + t2s->n_pairs, exact ? 5 : 3, 0);
+  struct tab_table *table = tab_create (1 + t2s->n_pairs, exact ? 5 : 3);
 
-  tab_dim (table, tab_natural_dimensions, NULL);
+  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, _("Test Statistics"));
 
@@ -316,11 +316,11 @@ show_tests_box (const struct wilcoxon_state *ws,
 
   /* Vertical lines inside the box */
   tab_box (table, 0, 0, -1, TAL_1,
-          0, 0, table->nc - 1, tab_nr (table) - 1 );
+          0, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
 
   /* Box around entire table */
   tab_box (table, TAL_2, TAL_2, -1, -1,
-          0, 0, table->nc - 1, tab_nr (table) - 1 );
+          0, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
 
 
   tab_text (table,  0, 1,  TAB_LEFT, _("Z"));
@@ -377,8 +377,8 @@ show_tests_box (const struct wilcoxon_state *ws,
        }
     }
 
-  tab_hline (table, TAL_2, 0, table->nc - 1, 1);
-  tab_vline (table, TAL_2, 1, 0, table->nr - 1);
+  tab_hline (table, TAL_2, 0, tab_nc (table) - 1, 1);
+  tab_vline (table, TAL_2, 1, 0, tab_nr (table) - 1);
 
 
   tab_submit (table);
index 0771ade3a9e92dccc0c3063a7c14d13014c010e3..9b5fd0430b56879fd92e1996c732cbe16e0c2068 100644 (file)
@@ -29,6 +29,7 @@
 #include <language/command.h>
 #include <language/lexer/lexer.h>
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <libpspp/message.h>
 #include <libpspp/message.h>
 #include <libpspp/str.h>
@@ -62,14 +63,16 @@ struct syntax_file_source
 static const char *
 name (const struct getl_interface *s)
 {
-  const struct syntax_file_source *sfs = (const struct syntax_file_source *) s;
+  const struct syntax_file_source *sfs = UP_CAST (s, struct syntax_file_source,
+                                                  parent);
   return sfs->fn;
 }
 
 static int
 line_number (const struct getl_interface *s)
 {
-  const struct syntax_file_source *sfs = (const struct syntax_file_source *) s;
+  const struct syntax_file_source *sfs = UP_CAST (s, struct syntax_file_source,
+                                                  parent);
   return sfs->ln;
 }
 
@@ -80,7 +83,8 @@ static bool
 read_syntax_file (struct getl_interface *s,
                   struct string *line)
 {
-  struct syntax_file_source *sfs = (struct syntax_file_source *) s;
+  struct syntax_file_source *sfs = UP_CAST (s, struct syntax_file_source,
+                                            parent);
 
   /* Open file, if not yet opened. */
   if (sfs->syntax_file == NULL)
@@ -121,7 +125,8 @@ read_syntax_file (struct getl_interface *s,
 static void
 syntax_close (struct getl_interface *s)
 {
-  struct syntax_file_source *sfs = (struct syntax_file_source *) s;
+  struct syntax_file_source *sfs = UP_CAST (s, struct syntax_file_source,
+                                            parent);
 
   if (sfs->syntax_file && EOF == fn_close (sfs->fn, sfs->syntax_file))
     msg (MW, _("Closing `%s': %s."), sfs->fn, strerror (errno));
@@ -151,6 +156,6 @@ create_syntax_file_source (const char *fn)
   ss->parent.name = name ;
   ss->parent.location = line_number;
 
-  return (struct getl_interface *) ss;
+  return &ss->parent;
 }
 
index 94a56f97aacb55ae642b3eac0290cd8531f2b2c3..405141cd50a7fd1734f387281b5b344d5a7e74f9 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical interface for PSPP.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 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
@@ -17,6 +17,7 @@
 
 #include <config.h>
 
+#include <libpspp/cast.h>
 #include <libpspp/getl.h>
 #include <libpspp/compiler.h>
 #include <libpspp/str.h>
@@ -60,7 +61,8 @@ location (const struct getl_interface *i UNUSED)
 static void
 do_close (struct getl_interface *i )
 {
-  struct syntax_string_source *sss = (struct syntax_string_source *) i;
+  struct syntax_string_source *sss = UP_CAST (i, struct syntax_string_source,
+                                              parent);
 
   ds_destroy (&sss->buffer);
 
@@ -73,7 +75,8 @@ static bool
 read_single_line (struct getl_interface *i,
                  struct string *line)
 {
-  struct syntax_string_source *sss = (struct syntax_string_source *) i;
+  struct syntax_string_source *sss = UP_CAST (i, struct syntax_string_source,
+                                              parent);
 
   size_t next;
 
@@ -120,7 +123,7 @@ create_syntax_string_source (const char *format, ...)
   sss->parent.location = location;
 
 
-  return (struct getl_interface *) sss;
+  return &sss->parent;
 }
 
 /* Return the syntax currently contained in S.
index 19e9f9e54bf36b860b46f8d4681ce61660931b10..3d3c2c40e73ebde043d0be174f37205e41b3d5f8 100644 (file)
@@ -33,9 +33,9 @@ cmd_echo (struct lexer *lexer, struct dataset *ds UNUSED)
   if (lex_token (lexer) != T_STRING)
     return CMD_FAILURE;
 
-  tab = tab_create(1, 1, 0);
+  tab = tab_create(1, 1);
 
-  tab_dim (tab, tab_natural_dimensions, NULL);
+  tab_dim (tab, tab_natural_dimensions, NULL, NULL);
   tab_flags (tab, SOMF_NO_TITLE );
 
   tab_text(tab, 0, 0, 0, ds_cstr (lex_tokstr (lexer)));
index 8ce2d12576697beb5562fc65fc4a03522ce2a9a0..4fb889b17717ae3a0dec8bc98ee5a81ea6e219b9 100644 (file)
@@ -352,7 +352,7 @@ count_trns_proc (void *trns_, struct ccase **c,
 static bool
 count_trns_free (void *trns_)
 {
-  struct count_trns *trns = (struct count_trns *) trns_;
+  struct count_trns *trns = trns_;
   pool_destroy (trns->pool);
   return true;
 }
index 74b2ceb387f54d04e669cfbadb7eb4e7589c89a3..b776612922f2579b290f3a3657f12adb8efee0fe 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 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
@@ -29,6 +29,7 @@
 #endif
 
 #include <libpspp/abt.h>
+#include <libpspp/cast.h>
 
 #include <stdbool.h>
 
@@ -145,8 +146,8 @@ insert_relative (struct abt *abt, const struct abt_node *p, bool after,
           p = p->down[dir];
           dir = !after;
         }
-      ((struct abt_node *) p)->down[dir] = node;
-      node->up = (struct abt_node *) p;
+      CONST_CAST (struct abt_node *, p)->down[dir] = node;
+      node->up = CONST_CAST (struct abt_node *, p);
       abt_reaugmented (abt, node);
     }
 
@@ -280,7 +281,7 @@ abt_find (const struct abt *abt, const struct abt_node *target)
     {
       cmp = abt->compare (target, p, abt->aux);
       if (cmp == 0)
-        return (struct abt_node *) p;
+        return CONST_CAST (struct abt_node *, p);
     }
 
   return NULL;
@@ -307,7 +308,7 @@ abt_next (const struct abt *abt, const struct abt_node *p)
       p = p->down[1];
       while (p->down[0] != NULL)
         p = p->down[0];
-      return (struct abt_node *) p;
+      return CONST_CAST (struct abt_node *, p);
     }
 }
 
@@ -332,7 +333,7 @@ abt_prev (const struct abt *abt, const struct abt_node *p)
       p = p->down[0];
       while (p->down[1] != NULL)
         p = p->down[1];
-      return (struct abt_node *) p;
+      return CONST_CAST (struct abt_node *, p);
     }
 }
 
index 3801eb86636c14d2d4a73420329d7c568768d630..24d7268a2d6543a383361c50914d796e52e7b6c1 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 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
    tree paper.  */
 
 #include <stddef.h>
+#include <libpspp/cast.h>
 
 /* Returns the data structure corresponding to the given NODE,
    assuming that NODE is embedded as the given MEMBER name in
    data type STRUCT. */
-#define abt_data(NODE, STRUCT, MEMBER)                                  \
-        ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+#define abt_data(NODE, STRUCT, MEMBER)                          \
+        (CHECK_POINTER_HAS_TYPE (NODE, struct abt_node *),      \
+         UP_CAST (NODE, STRUCT, MEMBER))
 
 /* Node in an augmented binary tree. */
 struct abt_node
index 26eb982b25d47c26c9699c24018a0910cb2dbcd5..751c8fe764cd6f71aeec94be3550855f8dd3ce6e 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 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
@@ -67,6 +67,8 @@
 #include <stdbool.h>
 #include <stdint.h>
 
+#include <libpspp/cast.h>
+
 static void rebalance_subtree (struct bt *, struct bt_node *, size_t);
 
 static struct bt_node **down_link (struct bt *, struct bt_node *);
@@ -250,7 +252,7 @@ bt_find (const struct bt *bt, const struct bt_node *target)
     {
       cmp = bt->compare (target, p, bt->aux);
       if (cmp == 0)
-        return (struct bt_node *) p;
+        return CONST_CAST (struct bt_node *, p);
     }
 
   return NULL;
@@ -283,7 +285,7 @@ bt_find_ge (const struct bt *bt, const struct bt_node *target)
             break;
         }
     }
-  return (struct bt_node *) q;
+  return CONST_CAST (struct bt_node *, q);
 }
 
 /* Searches BT for, and returns, the last node in in-order whose
@@ -314,7 +316,7 @@ bt_find_le (const struct bt *bt, const struct bt_node *target)
             break;
         }
     }
-  return (struct bt_node *) q;
+  return CONST_CAST (struct bt_node *, q);
 }
 
 /* Returns the node in BT following P in in-order.
@@ -338,7 +340,7 @@ bt_next (const struct bt *bt, const struct bt_node *p)
       p = p->down[1];
       while (p->down[0] != NULL)
         p = p->down[0];
-      return (struct bt_node *) p;
+      return CONST_CAST (struct bt_node *, p);
     }
 }
 
@@ -363,7 +365,7 @@ bt_prev (const struct bt *bt, const struct bt_node *p)
       p = p->down[0];
       while (p->down[1] != NULL)
         p = p->down[1];
-      return (struct bt_node *) p;
+      return CONST_CAST (struct bt_node *, p);
     }
 }
 
index 340b8760166c31fec29ea42ae8ecc50c24911806..7045632ef204e2edd228dc4dd8473174318212be 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 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
    fully encapsulated. */
 
 #include <stddef.h>
+#include <libpspp/cast.h>
 
 /* Returns the data structure corresponding to the given NODE,
    assuming that NODE is embedded as the given MEMBER name in
    data type STRUCT. */
-#define bt_data(NODE, STRUCT, MEMBER)                                  \
-        ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+#define bt_data(NODE, STRUCT, MEMBER)                           \
+        (CHECK_POINTER_HAS_TYPE (NODE, struct bt_node *),       \
+         UP_CAST (NODE, STRUCT, MEMBER))
 
 /* Node in a balanced binary tree. */
 struct bt_node
diff --git a/src/libpspp/cast.h b/src/libpspp/cast.h
new file mode 100644 (file)
index 0000000..1e33857
--- /dev/null
@@ -0,0 +1,92 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef LIBPSPP_CAST_H
+#define LIBPSPP_CAST_H 1
+
+#include <stddef.h>
+
+/* Expands to a void expression that checks that POINTER is an
+   expression whose type is a qualified or unqualified version of
+   a type compatible with TYPE (a pointer type) and, if not,
+   causes a compiler warning to be issued (on typical compilers).
+
+   Examples:
+
+   int *ip;
+   const int *cip;
+   const int **cipp;
+   int ***ippp;
+   double *dp;
+
+   // None of these causes a warning:
+   CHECK_POINTER_HAS_TYPE (ip, int *);
+   CHECK_POINTER_HAS_TYPE (ip, const int *);
+   CHECK_POINTER_HAS_TYPE (cip, int *);
+   CHECK_POINTER_HAS_TYPE (cip, const int *);
+   CHECK_POINTER_HAS_TYPE (dp, double *);
+   CHECK_POINTER_HAS_TYPE (dp, const double *);
+   CHECK_POINTER_HAS_TYPE (cipp, const int **);
+   CHECK_POINTER_HAS_TYPE (cipp, const int *const *);
+   CHECK_POINTER_HAS_TYPE (ippp, int ***);
+   CHECK_POINTER_HAS_TYPE (ippp, int **const *);
+
+   // None of these causes a warning either, although it is unusual to
+   // const-qualify a pointer like this (it's like declaring a "const int",
+   // for example).
+   CHECK_POINTER_HAS_TYPE (ip, int *const);
+   CHECK_POINTER_HAS_TYPE (ip, const int *const);
+   CHECK_POINTER_HAS_TYPE (cip, int *const);
+   CHECK_POINTER_HAS_TYPE (cip, const int *const);
+   CHECK_POINTER_HAS_TYPE (cipp, const int **const);
+   CHECK_POINTER_HAS_TYPE (cipp, const int *const *const);
+   CHECK_POINTER_HAS_TYPE (ippp, int ***const);
+   CHECK_POINTER_HAS_TYPE (ippp, int **const *const);
+
+   // Provokes a warning because "int" is not compatible with "double":
+   CHECK_POINTER_HAS_TYPE (dp, int *);
+
+   // Provoke warnings because C's type compatibility rules only allow
+   // adding a "const" qualifier to the outermost pointer:
+   CHECK_POINTER_HAS_TYPE (ippp, const int ***);
+   CHECK_POINTER_HAS_TYPE (ippp, int *const**);
+*/
+#define CHECK_POINTER_HAS_TYPE(POINTER, TYPE)           \
+        ((void) sizeof ((TYPE) (POINTER) == (POINTER)))
+
+/* Given expressions A and B, both of which have pointer type,
+   expands to a void expression that causes a compiler warning if
+   A and B are not pointers to qualified or unqualified versions
+   of compatible types.
+
+   Examples similar to those given for CHECK_POINTER_HAS_TYPE,
+   above, can easily be devised. */
+#define CHECK_POINTER_COMPATIBILITY(A, B) ((void) sizeof ((A) == (B)))
+
+/* Equivalent to casting POINTER to TYPE, but also issues a
+   warning if the cast changes anything other than an outermost
+   "const" or "volatile" qualifier. */
+#define CONST_CAST(TYPE, POINTER)                       \
+        (CHECK_POINTER_HAS_TYPE (POINTER, TYPE),        \
+         (TYPE) (POINTER))
+
+/* Given POINTER, a pointer to the given MEMBER within structure
+   STRUCT, returns the address of the STRUCT. */
+#define UP_CAST(POINTER, STRUCT, MEMBER)                                \
+        (CHECK_POINTER_COMPATIBILITY (&((STRUCT *) 0)->MEMBER, POINTER), \
+         (STRUCT *) ((char *) (POINTER) - offsetof (STRUCT, MEMBER)))
+
+#endif /* libpspp/cast.h */
index 57fc2678d09548ad50922b9130298afb6cf80cac..e586bcc0c18d1ae79242dc5a6ba42068db5b1f63 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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
index 134224373d2ec2dbf6536b4d7909799df59b06d6..3a3c516a03485ab7e4a5ecac923719c3425c4ee0 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 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
@@ -70,6 +70,7 @@
 #ifndef LIBPSPP_HEAP_H
 #define LIBPSPP_HEAP_H 1
 
+#include <libpspp/cast.h>
 #include <stdbool.h>
 #include <stddef.h>
 
@@ -78,8 +79,9 @@ struct pool;
 /* Returns the data structure corresponding to the given heap
    NODE, assuming that NODE is embedded as the given MEMBER name
    in data type STRUCT. */
-#define heap_data(NODE, STRUCT, MEMBER)                                 \
-        ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+#define heap_data(NODE, STRUCT, MEMBER)                         \
+        (CHECK_POINTER_HAS_TYPE (NODE, struct heap_node *),     \
+         UP_CAST (NODE, STRUCT, MEMBER))
 
 /* A node in a heap.  Opaque.
    One of these structures must be embedded in your heap node. */
index e73d84fd153764ec96935c6242dc2388820c5757..c9e764dec563a92df08491839259e5f061884963 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 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
    */
 
 #include <stddef.h>
+#include <libpspp/cast.h>
 
 /* Returns the data structure corresponding to the given NODE,
    assuming that NODE is embedded as the given MEMBER name in
    data type STRUCT.  NODE must not be a null pointer. */
 #define HMAP_DATA(NODE, STRUCT, MEMBER)                         \
-  ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+        (CHECK_POINTER_HAS_TYPE (NODE, struct hmap_node *),     \
+         UP_CAST (NODE, STRUCT, MEMBER))
 
 /* Like HMAP_DATA, except that a null NODE yields a null pointer
    result. */
index b9d595bc97a00212e9ebc1c156a1a01f75ed1556..e6533f2cd25fccf5beb2cbf9a4da31c279022027 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
+   Copyright (C) 2006, 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
@@ -169,7 +169,7 @@ ll_find_equal (const struct ll *r0, const struct ll *r1,
   for (x = r0; x != r1; x = ll_next (x))
     if (compare (x, target, aux) == 0)
       break;
-  return (struct ll *) x;
+  return CONST_CAST (struct ll *, x);
 }
 
 /* Returns the first node in R0...R1 for which PREDICATE returns
@@ -185,7 +185,7 @@ ll_find_if (const struct ll *r0, const struct ll *r1,
   for (x = r0; x != r1; x = ll_next (x))
     if (predicate (x, aux))
       break;
-  return (struct ll *) x;
+  return CONST_CAST (struct ll *, x);
 }
 
 /* Compares each pair of adjacent nodes in R0...R1
@@ -203,10 +203,10 @@ ll_find_adjacent_equal (const struct ll *r0, const struct ll *r1,
 
       for (x = r0, y = ll_next (x); y != r1; x = y, y = ll_next (y))
         if (compare (x, y, aux) == 0)
-          return (struct ll *) x;
+          return CONST_CAST (struct ll *, x);
     }
 
-  return (struct ll *) r1;
+  return CONST_CAST (struct ll *, r1);
 }
 
 /* Returns the number of nodes in R0...R1.
@@ -272,7 +272,7 @@ ll_max (const struct ll *r0, const struct ll *r1,
         if (compare (x, max, aux) > 0)
           max = x;
     }
-  return (struct ll *) max;
+  return CONST_CAST (struct ll *, max);
 }
 
 /* Returns the least node in R0...R1 according to COMPARE given
@@ -291,7 +291,7 @@ ll_min (const struct ll *r0, const struct ll *r1,
         if (compare (x, min, aux) < 0)
           min = x;
     }
-  return (struct ll *) min;
+  return CONST_CAST (struct ll *, min);
 }
 
 /* Lexicographically compares A0...A1 to B0...B1.
@@ -474,7 +474,7 @@ ll_find_run (const struct ll *r0, const struct ll *r1,
       while (r0 != r1 && compare (ll_prev (r0), r0, aux) <= 0);
     }
 
-  return (struct ll *) r0;
+  return CONST_CAST (struct ll *, r0);
 }
 
 /* Merges B0...B1 into A0...A1 according to COMPARE given
@@ -681,6 +681,6 @@ ll_find_partition (const struct ll *r0, const struct ll *r1,
     if (predicate (x, aux))
       return NULL;
 
-  return (struct ll *) partition;
+  return CONST_CAST (struct ll *, partition);
 }
 \f
index 65ecf55f2cad759a50085a85e90f1e3c79cf7f02..bf871f6b0f842a0a42726de48364aad08ea4d3ea 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
+   Copyright (C) 2006, 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
@@ -50,6 +50,9 @@
 #include <assert.h>
 #include <stdbool.h>
 #include <stddef.h>
+#include <libpspp/cast.h>
+
+#include <libpspp/cast.h>
 
 /* Embedded, circular doubly linked list.
 
 /* Returns the data structure corresponding to the given node LL,
    assuming that LL is embedded as the given MEMBER name in data
    type STRUCT. */
-#define ll_data(LL, STRUCT, MEMBER)                                    \
-        ((STRUCT *) ((char *) (LL) - offsetof (STRUCT, MEMBER)))
+#define ll_data(LL, STRUCT, MEMBER)                     \
+        (CHECK_POINTER_HAS_TYPE(LL, struct ll *),       \
+         UP_CAST(LL, STRUCT, MEMBER))
 
 /* Linked list node. */
 struct ll
@@ -378,7 +382,7 @@ ll_tail (const struct ll_list *list)
 static inline struct ll *
 ll_null (const struct ll_list *list)
 {
-  return (struct ll *) &list->null;
+  return CONST_CAST (struct ll *, &list->null);
 }
 
 /* Returns the node following LL in its list,
index 4dc82bffeb66219bf37d12f7cff7131a0c73cccc..c58f840c40a6db106d250e0cb6b5a9dec39efa5f 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 Free Software Foundation, Inc.
+   Copyright (C) 2006, 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
@@ -232,7 +232,7 @@ llx_find_equal (const struct llx *r0, const struct llx *r1,
   for (x = r0; x != r1; x = llx_next (x))
     if (compare (llx_data (x), target, aux) == 0)
       break;
-  return (struct llx *) x;
+  return CONST_CAST (struct llx *, x);
 }
 
 /* Returns the first node in R0...R1 for which PREDICATE returns
@@ -248,7 +248,7 @@ llx_find_if (const struct llx *r0, const struct llx *r1,
   for (x = r0; x != r1; x = llx_next (x))
     if (predicate (llx_data (x), aux))
       break;
-  return (struct llx *) x;
+  return CONST_CAST (struct llx *, x);
 }
 
 /* Compares each pair of adjacent nodes in R0...R1
@@ -266,10 +266,10 @@ llx_find_adjacent_equal (const struct llx *r0, const struct llx *r1,
 
       for (x = r0, y = llx_next (x); y != r1; x = y, y = llx_next (y))
         if (compare (llx_data (x), llx_data (y), aux) == 0)
-          return (struct llx *) x;
+          return CONST_CAST (struct llx *, x);
     }
 
-  return (struct llx *) r1;
+  return CONST_CAST (struct llx *, r1);
 }
 
 /* Returns the number of nodes in R0...R1.
@@ -329,7 +329,7 @@ llx_max (const struct llx *r0, const struct llx *r1,
         if (compare (llx_data (x), llx_data (max), aux) > 0)
           max = x;
     }
-  return (struct llx *) max;
+  return CONST_CAST (struct llx *, max);
 }
 
 /* Returns the least node in R0...R1 according to COMPARE given
@@ -348,7 +348,7 @@ llx_min (const struct llx *r0, const struct llx *r1,
         if (compare (llx_data (x), llx_data (min), aux) < 0)
           min = x;
     }
-  return (struct llx *) min;
+  return CONST_CAST (struct llx *, min);
 }
 
 /* Lexicographically compares A0...A1 to B0...B1.
@@ -521,7 +521,7 @@ llx_find_run (const struct llx *r0, const struct llx *r1,
                                   llx_data (r0), aux) <= 0);
     }
 
-  return (struct llx *) r0;
+  return CONST_CAST (struct llx *, r0);
 }
 
 /* Merges B0...B1 into A0...A1 according to COMPARE given
@@ -734,7 +734,7 @@ llx_find_partition (const struct llx *r0, const struct llx *r1,
     if (predicate (llx_data (x), aux))
       return NULL;
 
-  return (struct llx *) partition;
+  return CONST_CAST (struct llx *, partition);
 }
 \f
 /* Allocates and returns a node using malloc. */
index 3dd77c9ffac30f2fb85429df8a35091ce108c7ef..024fe93790e507a052e14e4990152b9b17c7ea91 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 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
 #include <stdbool.h>
 
 #include <libpspp/bt.h>
+#include <libpspp/cast.h>
 
 /* Returns the data structure corresponding to the given NODE,
    assuming that NODE is embedded as the given MEMBER name in
    data type STRUCT. */
 #define range_map_data(NODE, STRUCT, MEMBER)                            \
-        ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+        (CHECK_POINTER_HAS_TYPE (NODE, struct range_map_node *),        \
+         UP_CAST (NODE, STRUCT, MEMBER))
 
 /* A range map node, to be embedded in the data value. */
 struct range_map_node
index efa3b4d670e7e06c3c560ebc64ade1bf4d45ae21..cd17fe02a4e2130544d5d37687cf46210ff2de39 100644 (file)
@@ -287,7 +287,7 @@ range_set_allocate_fully (struct range_set *rs, unsigned long int request,
 bool
 range_set_contains (const struct range_set *rs_, unsigned long int position)
 {
-  struct range_set *rs = (struct range_set *) rs_;
+  struct range_set *rs = CONST_CAST (struct range_set *, rs_);
   if (position < rs->cache_end && position >= rs->cache_start)
     return rs->cache_value;
   else
@@ -328,7 +328,7 @@ range_set_contains (const struct range_set *rs_, unsigned long int position)
 unsigned long int
 range_set_scan (const struct range_set *rs_, unsigned long int start)
 {
-  struct range_set *rs = (struct range_set *) rs_;
+  struct range_set *rs = CONST_CAST (struct range_set *, rs_);
   unsigned long int retval = ULONG_MAX;
   struct bt_node *bt_node;
 
index 941692b40029098ea8224ea4d0a4f0e1b9026385..ee7dac23137d3c39b392b13cb11c7d560c71fc79 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <stdbool.h>
 #include <libpspp/bt.h>
+#include <libpspp/cast.h>
 
 /* A set of ranges. */
 struct range_set
@@ -122,7 +123,7 @@ static inline const struct range_set_node *
 range_set_next (const struct range_set *rs, const struct range_set_node *node)
 {
   return (node != NULL
-          ? range_set_next__ (rs, (struct range_set_node *) node)
+          ? range_set_next__ (rs, CONST_CAST (struct range_set_node *, node))
           : range_set_first__ (rs));
 }
 
@@ -147,7 +148,7 @@ static inline const struct range_set_node *
 range_set_prev (const struct range_set *rs, const struct range_set_node *node)
 {
   return (node != NULL
-          ? range_set_prev__ (rs, (struct range_set_node *) node)
+          ? range_set_prev__ (rs, CONST_CAST (struct range_set_node *, node))
           : range_set_last__ (rs));
 }
 
index 28398d5cc86321e9e648a9c0ac30c052c1d26641..298f2caf9eb9ae09108aa247fc6db7b000183de1 100644 (file)
@@ -22,6 +22,7 @@
 #include <string.h>
 
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <libpspp/misc.h>
 #include <libpspp/pool.h>
 
@@ -577,7 +578,7 @@ leaf_size (const struct sparse_array *spar)
 static struct leaf_node *
 find_leaf_node (const struct sparse_array *spar_, unsigned long int key)
 {
-  struct sparse_array *spar = (struct sparse_array *) spar_;
+  struct sparse_array *spar = CONST_CAST (struct sparse_array *, spar_);
   const union pointer *p;
   int level;
 
@@ -679,7 +680,7 @@ static void *
 scan_forward (const struct sparse_array *spar_, unsigned long int start,
               unsigned long int *found)
 {
-  struct sparse_array *spar = (struct sparse_array *) spar_;
+  struct sparse_array *spar = CONST_CAST (struct sparse_array *, spar_);
 
   /* Check the cache. */
   if (start >> BITS_PER_LEVEL == spar->cache_ofs)
@@ -761,7 +762,7 @@ static void *
 scan_reverse (const struct sparse_array *spar_, unsigned long int start,
               unsigned long int *found)
 {
-  struct sparse_array *spar = (struct sparse_array *) spar_;
+  struct sparse_array *spar = CONST_CAST (struct sparse_array *, spar_);
 
   /* Check the cache. */
   if (start >> BITS_PER_LEVEL == spar->cache_ofs)
index afe32de9f2049bfcc5a80ac7a95b36b348be4cb7..8243aa0690aad24a9b519db9bd1838724d32d729 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 
+#include <libpspp/cast.h>
 #include <libpspp/message.h>
 #include <libpspp/pool.h>
 
@@ -1214,7 +1215,7 @@ ds_capacity (const struct string *st)
 char *
 ds_cstr (const struct string *st_)
 {
-  struct string *st = (struct string *) st_;
+  struct string *st = CONST_CAST (struct string *, st_);
   if (st->ss.string == NULL)
     ds_extend (st, 1);
   st->ss.string[st->ss.length] = '\0';
index 3a74587bb5918ae8cee61766619187b5ca758532..4c1cecb97cdbbdbcdd2302e91f2741e7a68a8bc2 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 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
@@ -22,6 +22,7 @@
 
 #include <libpspp/array.h>
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 
 #include "xalloc.h"
 
@@ -79,7 +80,7 @@ taint_create (void)
 struct taint *
 taint_clone (const struct taint *taint_)
 {
-  struct taint *taint = (struct taint *) taint_;
+  struct taint *taint = CONST_CAST (struct taint *, taint_);
 
   assert (taint->ref_cnt > 0);
   taint->ref_cnt++;
@@ -139,8 +140,8 @@ taint_destroy (struct taint *taint)
 void
 taint_propagate (const struct taint *from_, const struct taint *to_)
 {
-  struct taint *from = (struct taint *) from_;
-  struct taint *to = (struct taint *) to_;
+  struct taint *from = CONST_CAST (struct taint *, from_);
+  struct taint *to = CONST_CAST (struct taint *, to_);
 
   if (from != to)
     {
@@ -165,7 +166,7 @@ taint_is_tainted (const struct taint *taint)
 void
 taint_set_taint (const struct taint *taint_)
 {
-  struct taint *taint = (struct taint *) taint_;
+  struct taint *taint = CONST_CAST (struct taint *, taint_);
   if (!taint->tainted)
     recursively_set_taint (taint);
 }
@@ -186,7 +187,7 @@ taint_has_tainted_successor (const struct taint *taint)
 void
 taint_reset_successor_taint (const struct taint *taint_)
 {
-  struct taint *taint = (struct taint *) taint_;
+  struct taint *taint = CONST_CAST (struct taint *, taint_);
 
   if (taint->tainted_successor)
     {
index 0d3093646faa6a9d20a941048760f0d0d11d8be6..aff135d0cc0a1c27b0ea9af1d58dec6beb08d19a 100644 (file)
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 
 #include "error.h"
 #include "xalloc.h"
@@ -81,7 +82,7 @@ tmpfile_destroy (struct tmpfile *tf)
 static bool
 do_seek (const struct tmpfile *tf_, off_t offset)
 {
-  struct tmpfile *tf = (struct tmpfile *) tf_;
+  struct tmpfile *tf = CONST_CAST (struct tmpfile *, tf_);
 
   if (!tmpfile_error (tf))
     {
@@ -106,7 +107,7 @@ do_seek (const struct tmpfile *tf_, off_t offset)
 static bool
 do_read (const struct tmpfile *tf_, void *buffer, size_t bytes)
 {
-  struct tmpfile *tf = (struct tmpfile *) tf_;
+  struct tmpfile *tf = CONST_CAST (struct tmpfile *, tf_);
 
   assert (!tmpfile_error (tf));
   if (bytes > 0 && fread (buffer, bytes, 1, tf->file) != 1)
index e8d253d086731c61dd539962cbc79f9c2b3b0750..9157987774e555c625d63af41a9343dc4447c574 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 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
@@ -21,6 +21,7 @@
 #include <limits.h>
 
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <libpspp/compiler.h>
 
 static struct tower_node *abt_to_tower_node (const struct abt_node *);
@@ -184,7 +185,7 @@ tower_lookup (const struct tower *t_,
               unsigned long height,
               unsigned long *node_start)
 {
-  struct tower *t = (struct tower *) t_;
+  struct tower *t = CONST_CAST (struct tower *, t_);
   struct abt_node *p;
 
   assert (height < tower_height (t));
@@ -237,7 +238,7 @@ tower_lookup (const struct tower *t_,
 struct tower_node *
 tower_get (const struct tower *t_, unsigned long int index) 
 {
-  struct tower *t = (struct tower *) t_;
+  struct tower *t = CONST_CAST (struct tower *, t_);
   struct abt_node *p;
 
   assert (index < tower_count (t));
index 246984a2c1bfd6cd1c46100fbe95f84140843a92..9be8231c98ad7c9b0c7fc217a14a513d2813aa92 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 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
 
 #include <stdbool.h>
 #include <libpspp/abt.h>
+#include <libpspp/cast.h>
 
 /* Returns the data structure corresponding to the given NODE,
    assuming that NODE is embedded as the given MEMBER name in
    data type STRUCT. */
-#define tower_data(NODE, STRUCT, MEMBER)                            \
-        ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+#define tower_data(NODE, STRUCT, MEMBER)                        \
+        (CHECK_POINTER_HAS_TYPE (NODE, struct tower_node *),    \
+         UP_CAST (NODE, STRUCT, MEMBER))
 
 /* A node within a tower. */
 struct tower_node
index 288fc072ef119ccb2c49b2001a015a34834378ad..de4124efe16c5beb799c7b5d0b2af42743ff79d7 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 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
@@ -20,6 +20,7 @@
 #include "tukey-hinges.h"
 #include <gl/xalloc.h>
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <math.h>
 #include <float.h>
 #include <data/val-type.h>
@@ -30,8 +31,8 @@
 static void
 destroy (struct statistic *s)
 {
-  struct order_stats *os = (struct order_stats *) s;
-  struct box_whisker *bw = (struct box_whisker *) s;
+  struct box_whisker *bw = UP_CAST (s, struct box_whisker, parent.parent);
+  struct order_stats *os = &bw->parent;
   struct ll *ll;
 
   for (ll = ll_head (&bw->outliers); ll != ll_null (&bw->outliers); )
@@ -53,7 +54,7 @@ static void
 acc (struct statistic *s, const struct ccase *cx,
      double c UNUSED, double cc UNUSED, double y)
 {
-  struct box_whisker *bw = (struct box_whisker *) s;
+  struct box_whisker *bw = UP_CAST (s, struct box_whisker, parent.parent);
   bool extreme;
   struct outlier *o;
 
@@ -110,13 +111,13 @@ box_whisker_outliers (const struct box_whisker *bw)
   return &bw->outliers;
 }
 
-struct statistic *
+struct box_whisker *
 box_whisker_create (const struct tukey_hinges *th,
                    const struct variable *id_var,  size_t casenumber_idx)
 {
   struct box_whisker *w = xzalloc (sizeof (*w));
-  struct order_stats *os = (struct order_stats *) w;
-  struct statistic *stat = (struct statistic *) w;
+  struct order_stats *os = &w->parent;
+  struct statistic *stat = &os->parent;
 
   os->n_k = 0;
 
@@ -135,5 +136,5 @@ box_whisker_create (const struct tukey_hinges *th,
 
   ll_init (&w->outliers);
 
-  return stat;
+  return w;
 }
index 5202b646727b184ccc22b3ffcfd7e14f3c32c8fd..bef091e83b448dafc5c601fab3d04eb0d60f19f6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 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
@@ -52,7 +52,7 @@ struct box_whisker
   const struct variable *id_var;
 };
 
-struct statistic * box_whisker_create (const struct tukey_hinges *,
+struct box_whisker * box_whisker_create (const struct tukey_hinges *,
                                         const struct variable *, size_t);
 
 void box_whisker_whiskers (const struct box_whisker *bw, double whiskers[2]);
index 67079398d169ec58737c6f3b94a7d243b9fc8516..3c88c3858f61c6b1c2230843a7b9151a47902956 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 2008 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2008, 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
@@ -19,6 +19,7 @@
 
 #include <gl/xalloc.h>
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 
 #include <gsl/gsl_histogram.h>
 #include "chart-geometry.h"
@@ -28,7 +29,8 @@
 void
 histogram_add (struct histogram *h, double y, double c)
 {
-  ((struct statistic *)h)->accumulate ((struct statistic *) h, NULL, c, 0, y);
+  struct statistic *stat = &h->parent;
+  stat->accumulate (stat, NULL, c, 0, y);
 }
 
 
@@ -36,7 +38,7 @@ histogram_add (struct histogram *h, double y, double c)
 static void
 acc (struct statistic *s, const struct ccase *cx UNUSED, double c, double cc UNUSED, double y)
 {
-  struct histogram *hist = (struct histogram *) s;
+  struct histogram *hist = UP_CAST (s, struct histogram, parent);
 
   gsl_histogram_accumulate (hist->gsl_hist, y, c);
 }
@@ -45,17 +47,17 @@ acc (struct statistic *s, const struct ccase *cx UNUSED, double c, double cc UNU
 static void
 destroy (struct statistic *s)
 {
-  struct histogram *h = (struct histogram *) s;
+  struct histogram *h = UP_CAST (s, struct histogram, parent);
   gsl_histogram_free (h->gsl_hist);
   free (s);
 }
 
 
-struct statistic *
+struct histogram *
 histogram_create (int bins, double min, double max)
 {
   struct histogram *h = xmalloc (sizeof *h);
-  struct statistic *stat = (struct statistic *) h;
+  struct statistic *stat = &h->parent;
   double upper_limit, lower_limit;
 
   double bin_width = chart_rounded_tick ((max - min) / (double) bins);
@@ -78,6 +80,6 @@ histogram_create (int bins, double min, double max)
   stat->accumulate = acc;
   stat->destroy = destroy;
 
-  return stat;
+  return h;
 }
 
index b2b204ee808098c2bf378ef99e8419c6d4223988..bc4a5ae6c1354d9711a467ceba8cf904a875a8fa 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 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
@@ -30,7 +30,7 @@ struct histogram
   gsl_histogram *gsl_hist;
 };
 
-struct statistic * histogram_create (int bins, double max, double min);
+struct histogram * histogram_create (int bins, double max, double min);
 
 void histogram_add (struct histogram *h, double y, double c);
 
index 7bd582105f940c28ffeb26506d4c5fe941ff728d..f7e269991346cd0c6cb076eacec04bcbbd62590c 100644 (file)
@@ -246,9 +246,7 @@ levene2_precalc (struct levene_info *l)
       struct hsh_table *hash = group_proc_get (var)->group_hash;
 
 
-      for(g = (struct group_statistics *) hsh_first(hash,&hi);
-         g != 0 ;
-         g = (struct group_statistics *) hsh_next(hash,&hi) )
+      for (g = hsh_first(hash,&hi); g != 0; g = hsh_next(hash, &hi))
        {
          g->lz_mean = g->lz_total / g->n ;
        }
@@ -308,9 +306,7 @@ levene2_postcalc (struct levene_info *l)
       struct group_proc *gp = group_proc_get (var);
       struct hsh_table *hash = gp->group_hash;
 
-      for(g = (struct group_statistics *) hsh_first(hash,&hi);
-         g != 0 ;
-         g = (struct group_statistics *) hsh_next(hash,&hi) )
+      for (g = hsh_first(hash, &hi); g != 0; g = hsh_next(hash, &hi))
        {
          lz_numerator += g->n * pow2(g->lz_mean - l->lz[v].grand_mean );
        }
index e61bf58e117aaf915d596bc26b8875152f0f0deb..b631820903e6ebf4e41a536ebe7ea964924af918 100644 (file)
@@ -24,6 +24,7 @@
 #include <data/case.h>
 #include <data/casewriter.h>
 #include <libpspp/compiler.h>
+#include <libpspp/cast.h>
 #include <libpspp/misc.h>
 #include <math/moments.h>
 
@@ -32,8 +33,8 @@
 static void
 destroy (struct statistic *stat)
 {
-  struct order_stats *os = (struct order_stats *) stat;
-  free (os);
+  struct np *np = UP_CAST (stat, struct np, parent.parent);
+  free (np);
 }
 
 
@@ -42,7 +43,7 @@ acc (struct statistic *s, const struct ccase *cx UNUSED,
      double c, double cc, double y)
 {
   struct ccase *cp;
-  struct np *np = (struct np *) s;
+  struct np *np = UP_CAST (s, struct np, parent.parent);
   double rank = np->prev_cc + (c + 1) / 2.0;
 
   double ns = gsl_cdf_ugaussian_Pinv (rank / ( np->n + 1 ));
@@ -69,13 +70,13 @@ acc (struct statistic *s, const struct ccase *cx UNUSED,
   np->prev_cc = cc;
 }
 
-struct order_stats *
+struct np *
 np_create (const struct moments1 *m)
 {
   double variance;
   struct np *np = xzalloc (sizeof (*np));
-  struct statistic *stat = (struct statistic *) np;
-  struct order_stats *os = (struct order_stats *) np;
+  struct order_stats *os = &np->parent;
+  struct statistic *stat = &os->parent;
   struct caseproto *proto;
   int i;
 
@@ -98,5 +99,5 @@ np_create (const struct moments1 *m)
   stat->destroy = destroy;
   stat->accumulate = acc;
 
-  return os;
+  return np;
 }
index 7db51f73b223fbcf7ade345afec06c45b5687fb9..b5265bd41f72c57d5b895a578a428971957fb61a 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 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
@@ -54,6 +54,6 @@ struct np
 };
 
 
-struct order_stats * np_create (const struct moments1 *);
+struct np * np_create (const struct moments1 *);
 
 #endif
index 1b6aa131ea745ba6c0b55b33a54f3a6d39ceddbe..e550d2b2eb55f66fd4baea366e7a058abb849443 100644 (file)
@@ -90,7 +90,7 @@ update_k_values (const struct ccase *cx, double y_i, double c_i, double cc_i,
     {
       int k;
       struct order_stats *tos = os[j];
-      struct statistic  *stat = (struct statistic *) tos;
+      struct statistic  *stat = &tos->parent;
       for (k = 0 ; k < tos->n_k; ++k)
        {
          struct k *myk = &tos->k[k];
index bf99de163ffbaafe2af8711685e9a640a0256784..c76bb492ba767a90ffa2c03c8d35bf8a7d532ab6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 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
@@ -24,6 +24,7 @@
 #define N_(msgid) msgid
 
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <data/val-type.h>
 #include <gl/xalloc.h>
 #include <data/variable.h>
@@ -44,7 +45,7 @@ const char *const ptile_alg_desc[] = {
 double
 percentile_calculate (const struct percentile *ptl, enum pc_alg alg)
 {
-  struct percentile *mutable = (struct percentile *) ptl;
+  struct percentile *mutable = CONST_CAST (struct percentile *, ptl);
   const struct order_stats *os = &ptl->parent;
 
   assert (os->cc == ptl->w);
@@ -154,18 +155,19 @@ percentile_calculate (const struct percentile *ptl, enum pc_alg alg)
 static void
 destroy (struct statistic *stat)
 {
-  struct order_stats *os = (struct order_stats *) stat;
+  struct percentile *ptl = UP_CAST (stat, struct percentile, parent.parent);
+  struct order_stats *os = &ptl->parent;
   free (os->k);
-  free (os);
+  free (ptl);
 }
 
 
-struct order_stats *
+struct percentile *
 percentile_create (double p, double W)
 {
   struct percentile *ptl = xzalloc (sizeof (*ptl));
-  struct order_stats *os = (struct order_stats *) ptl;
-  struct statistic *stat = (struct statistic *) ptl;
+  struct order_stats *os = &ptl->parent;
+  struct statistic *stat = &os->parent;
 
   assert (p >= 0);
   assert (p <= 1.0);
@@ -186,6 +188,6 @@ percentile_create (double p, double W)
 
   stat->destroy = destroy;
 
-  return os;
+  return ptl;
 }
 
index 0dd09820945e1bafaee5a017ba3756cb4197783b..ff46bea6dce45611b88c621df4f7e86a30d97534 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 2008 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2008, 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
@@ -52,7 +52,7 @@ struct percentile
 /* Create the Pth percentile.
    W is the total sum of weights in the data set
 */
-struct order_stats *percentile_create (double p, double W);
+struct percentile *percentile_create (double p, double W);
 
 /* Return the value of the percentile */
 double percentile_calculate (const struct percentile *ptl, enum pc_alg alg);
index da3d4240e5b232533de6c5c31073b53adda49f48..d1cc6b708279098ab8d263b95923d9b31c968ed6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 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
@@ -20,6 +20,7 @@
 
 #include <gl/xalloc.h>
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <math.h>
 #include <data/val-type.h>
 
@@ -27,8 +28,8 @@
 static void
 acc (struct statistic *s, const struct ccase *cx UNUSED, double c, double cc, double y)
 {
-  struct trimmed_mean *tm = (struct trimmed_mean *) s;
-  struct order_stats *os = (struct order_stats *) s;
+  struct trimmed_mean *tm = UP_CAST (s, struct trimmed_mean, parent.parent);
+  struct order_stats *os = &tm->parent;
 
   if ( cc > os->k[0].tc && cc < os->k[1].tc)
       tm->sum += c * y;
@@ -40,17 +41,18 @@ acc (struct statistic *s, const struct ccase *cx UNUSED, double c, double cc, do
 static void
 destroy (struct statistic *s)
 {
-  struct order_stats *os = (struct order_stats *) s;
+  struct trimmed_mean *tm = UP_CAST (s, struct trimmed_mean, parent.parent);
+  struct order_stats *os = &tm->parent;
   free (os->k);
-  free (s);
+  free (tm);
 }
 
-struct statistic *
+struct trimmed_mean *
 trimmed_mean_create (double W, double tail)
 {
   struct trimmed_mean *tm = xzalloc (sizeof (*tm));
-  struct order_stats *os = (struct order_stats *) tm;
-  struct statistic *stat = (struct statistic *) tm;
+  struct order_stats *os = &tm->parent;
+  struct statistic *stat = &os->parent;
 
   os->n_k = 2;
   os->k = xcalloc (sizeof (*os->k), 2);
@@ -68,7 +70,7 @@ trimmed_mean_create (double W, double tail)
   tm->w = W;
   tm->tail = tail;
 
-  return stat;
+  return tm;
 }
 
 
index 9339cab983ff9fba971d2cd2703ee81f17be6f66..c667b1be7ffcd9f9486606022c521143f5f08e6e 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 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
@@ -36,7 +36,7 @@ struct trimmed_mean
   double tail;
 };
 
-struct statistic * trimmed_mean_create (double W, double c_min);
+struct trimmed_mean * trimmed_mean_create (double W, double c_min);
 double trimmed_mean_calculate (const struct trimmed_mean *);
 
 #endif
index 95a79c1d30026da280cbc6fa2542518c0ff4ad1f..22ab45210c38c98066eb4bb654663114cb496ddd 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 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
@@ -20,6 +20,7 @@
 
 #include <gl/xalloc.h>
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <math.h>
 
 void
@@ -59,19 +60,20 @@ tukey_hinges_calculate (const struct tukey_hinges *th, double hinge[3])
 static void
 destroy (struct statistic *s)
 {
-  struct order_stats *os = (struct order_stats *) s;
+  struct tukey_hinges *th = UP_CAST (s, struct tukey_hinges, parent.parent);
+  struct order_stats *os = &th->parent;
 
   free (os->k);
   free (s);
 };
 
-struct statistic *
+struct tukey_hinges *
 tukey_hinges_create (double W, double c_min)
 {
   double d;
   struct tukey_hinges *th = xzalloc (sizeof (*th));
-  struct order_stats *os = (struct order_stats *) th;
-  struct statistic *stat = (struct statistic *) th;
+  struct order_stats *os = &th->parent;
+  struct statistic *stat = &os->parent;
 
   assert (c_min >= 0);
 
@@ -97,5 +99,5 @@ tukey_hinges_create (double W, double c_min)
 
   stat->destroy = destroy;
 
-  return stat;
+  return th;
 }
index d87691f8b01a4dceb78fddfc9294c17053e8690b..4b509da1d4321a8396510fd4c08b40d801214ffe 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 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
@@ -27,7 +27,7 @@ struct tukey_hinges
   struct order_stats parent;
 };
 
-struct statistic * tukey_hinges_create (double W, double c_min);
+struct tukey_hinges * tukey_hinges_create (double W, double c_min);
 
 
 void tukey_hinges_calculate (const struct tukey_hinges *h, double hinge[3]);
index 29d7b76b2940d76dbc0af108827922785f8e71ce..b224b3ebea9e54bdfac5d6859a18793583b046f6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+   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
 #include <libpspp/pool.h>
 #include <libpspp/start-date.h>
 #include <libpspp/version.h>
+#include <output/chart-provider.h>
+#include <output/chart.h>
+#include <output/output.h>
 
-#include "chart.h"
 #include "error.h"
 #include "minmax.h"
-#include "output.h"
 #include "xalloc.h"
 
 #include "gettext.h"
@@ -44,7 +45,7 @@
    output-file="pspp.list"
    append=no|yes                If output-file exists, append to it?
    chart-files="pspp-#.png"     Name used for charts.
-   chart-type=png               Format of charts (use "none" to disable).
+   chart-type=png|none
 
    paginate=on|off              Formfeeds are desired?
    tab-width=8                  Width of a tab; 0 to not use tabs.
@@ -108,7 +109,7 @@ struct ascii_driver_ext
     bool squeeze_blank_lines;   /* Squeeze multiple blank lines into one? */
     enum emphasis_style emphasis; /* How to emphasize text. */
     int tab_width;             /* Width of a tab; 0 not to use tabs. */
-    const char *chart_type;     /* Type of charts to output; NULL for none. */
+    bool enable_charts;         /* Enable charts? */
     const char *chart_file_name; /* Name of files used for charts. */
 
     bool auto_width;            /* Use viewwidth as page width? */
@@ -133,15 +134,17 @@ struct ascii_driver_ext
 static void ascii_flush (struct outp_driver *);
 static int get_default_box_char (size_t idx);
 static bool update_page_size (struct outp_driver *, bool issue_error);
-static bool handle_option (struct outp_driver *this, const char *key,
+static bool handle_option (void *this, const char *key,
                            const struct string *val);
 
 static bool
-ascii_open_driver (struct outp_driver *this, struct substring options)
+ascii_open_driver (const char *name, int types, struct substring options)
 {
+  struct outp_driver *this;
   struct ascii_driver_ext *x;
   int i;
 
+  this = outp_allocate_driver (&ascii_class, name, types);
   this->width = 79;
   this->font_height = 1;
   this->prop_em_width = 1;
@@ -157,7 +160,7 @@ ascii_open_driver (struct outp_driver *this, struct substring options)
   x->emphasis = EMPH_BOLD;
   x->tab_width = 8;
   x->chart_file_name = pool_strdup (x->pool, "pspp-#.png");
-  x->chart_type = pool_strdup (x->pool, "png");
+  x->enable_charts = true;
   x->auto_width = false;
   x->auto_length = false;
   x->page_length = 66;
@@ -172,9 +175,9 @@ ascii_open_driver (struct outp_driver *this, struct substring options)
   x->page_number = 0;
   x->lines = NULL;
   x->line_cap = 0;
-  x->chart_cnt = 0;
+  x->chart_cnt = 1;
 
-  if (!outp_parse_options (options, handle_option, this))
+  if (!outp_parse_options (this->name, options, handle_option, this))
     goto error;
 
   if (!update_page_size (this, true))
@@ -189,10 +192,13 @@ ascii_open_driver (struct outp_driver *this, struct substring options)
         x->box[i] = pool_strdup (x->pool, s);
       }
 
+  outp_register_driver (this);
+
   return true;
 
  error:
   pool_destroy (x->pool);
+  outp_free_driver (this);
   return false;
 }
 
@@ -312,9 +318,10 @@ static const struct outp_option option_tab[] =
   };
 
 static bool
-handle_option (struct outp_driver *this, const char *key,
+handle_option (void *this_, const char *key,
                const struct string *val)
 {
+  struct outp_driver *this = this_;
   struct ascii_driver_ext *x = this->ext;
   int subcat;
   const char *value;
@@ -478,10 +485,16 @@ handle_option (struct outp_driver *this, const char *key,
             error (0, 0, _("`chart-files' value must contain `#'"));
           break;
         case 2:
-          if (value[0] != '\0')
-            x->chart_type = pool_strdup (x->pool, value);
+          if (!strcmp (value, "png"))
+            x->enable_charts = true;
+          else if (!strcmp (value, "none"))
+            x->enable_charts = false;
           else
-            x->chart_type = NULL;
+            {
+              error (0, 0,
+                     _("ascii: `png' or `none' expected for `chart-type'"));
+              return false;
+            }
           break;
         case 3:
           x->init = pool_strdup (x->pool, value);
@@ -603,15 +616,6 @@ ascii_line (struct outp_driver *this,
     }
 }
 
-static void
-ascii_submit (struct outp_driver *this UNUSED, struct som_entity *s)
-{
-  extern struct som_table_class tab_table_class;
-
-  assert (s->class == &tab_table_class);
-  assert (s->type == SOM_CHART);
-}
-
 static void
 text_draw (struct outp_driver *this,
            enum outp_font font,
@@ -866,19 +870,15 @@ ascii_flush (struct outp_driver *this)
 }
 
 static void
-ascii_chart_initialise (struct outp_driver *this, struct chart *ch)
+ascii_output_chart (struct outp_driver *this, const struct chart *chart)
 {
   struct ascii_driver_ext *x = this->ext;
   struct outp_text t;
+  char *file_name;
   char *text;
 
-  if (x->chart_type == NULL)
-    return;
-
-  /* Initialize chart. */
-  chart_init_separate (ch, x->chart_type, x->chart_file_name, ++x->chart_cnt);
-  if (ch->file_name == NULL)
-    return;
+  /* Draw chart into separate file */
+  file_name = chart_draw_png (chart, x->chart_file_name, x->chart_cnt++);
 
   /* Mention chart in output.
      First advance current position. */
@@ -895,7 +895,7 @@ ascii_chart_initialise (struct outp_driver *this, struct chart *ch)
     }
 
   /* Then write the text. */
-  text = xasprintf ("See %s for a chart.", ch->file_name);
+  text = xasprintf ("See %s for a chart.", file_name);
   t.font = OUTP_FIXED;
   t.justification = OUTP_LEFT;
   t.string = ss_cstr (text);
@@ -906,17 +906,10 @@ ascii_chart_initialise (struct outp_driver *this, struct chart *ch)
   ascii_text_draw (this, &t);
   this->cp_y++;
 
+  free (file_name);
   free (text);
 }
 
-static void
-ascii_chart_finalise (struct outp_driver *this, struct chart *ch)
-{
-  struct ascii_driver_ext *x = this->ext;
-  if (x->chart_type != NULL)
-    chart_finalise_separate (ch);
-}
-
 const struct outp_class ascii_class =
 {
   "ascii",
@@ -929,12 +922,11 @@ const struct outp_class ascii_class =
   ascii_close_page,
   ascii_flush,
 
-  ascii_submit,
+  ascii_output_chart,
+
+  NULL,                         /* submit */
 
   ascii_line,
   ascii_text_metrics,
   ascii_text_draw,
-
-  ascii_chart_initialise,
-  ascii_chart_finalise
 };
index ec92c559e044723180977db31ca43708c7f637bd..eb9f7b78e78ddcd9194020fe18049ca2676cf6bc 100644 (file)
@@ -1,35 +1,43 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
-
-include $(top_srcdir)/src/output/charts/automake.mk
-
 noinst_LTLIBRARIES += src/output/liboutput.la 
 
-output_sources = \
+src_output_liboutput_la_CPPFLAGS = $(LIBXML2_CFLAGS) $(AM_CPPFLAGS) 
+
+src_output_liboutput_la_SOURCES = \
        src/output/afm.c \
        src/output/afm.h \
        src/output/ascii.c \
+       src/output/chart.c \
+       src/output/chart.h \
+       src/output/charts/box-whisker.c \
+       src/output/charts/box-whisker.h \
+       src/output/charts/cartesian.c \
+       src/output/charts/cartesian.h \
+       src/output/charts/np-plot.c \
+       src/output/charts/np-plot.h \
+       src/output/charts/piechart.c \
+       src/output/charts/piechart.h \
+       src/output/charts/plot-chart.c \
+       src/output/charts/plot-chart.h \
+       src/output/charts/plot-hist.c \
+       src/output/charts/plot-hist.h \
+       src/output/charts/roc-chart.c \
+       src/output/charts/roc-chart.h \
        src/output/html.c \
        src/output/htmlP.h \
        src/output/journal.c \
        src/output/journal.h \
+       src/output/manager.c \
+       src/output/manager.h \
+       src/output/odt.c \
        src/output/output.c \
        src/output/output.h \
        src/output/postscript.c \
-       src/output/manager.c \
-       src/output/manager.h \
-       src/output/chart.h \
-       src/output/table.c src/output/table.h
-
-
-if WITHCHARTS
-src_output_liboutput_la_SOURCES = $(output_sources) src/output/chart.c
-
-EXTRA_DIST += src/output/dummy-chart.c
-else
-src_output_liboutput_la_SOURCES = $(output_sources) src/output/dummy-chart.c
-
-EXTRA_DIST += src/output/chart.c
+       src/output/table.c \
+       src/output/table.h
+if HAVE_CAIRO
+src_output_liboutput_la_SOURCES += src/output/cairo.c
 endif
 
 EXTRA_DIST += src/output/OChangeLog
diff --git a/src/output/cairo.c b/src/output/cairo.c
new file mode 100644 (file)
index 0000000..b0b4f7d
--- /dev/null
@@ -0,0 +1,911 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/cairo.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/start-date.h>
+#include <libpspp/version.h>
+#include <output/afm.h>
+#include <output/chart-provider.h>
+#include <output/manager.h>
+#include <output/output.h>
+
+#include <cairo/cairo-pdf.h>
+#include <cairo/cairo-ps.h>
+#include <cairo/cairo-svg.h>
+#include <cairo/cairo.h>
+#include <pango/pango-font.h>
+#include <pango/pango-layout.h>
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
+#include <stdlib.h>
+
+#include "error.h"
+#include "intprops.h"
+#include "minmax.h"
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* Cairo driver options: (defaults listed first)
+
+   output-file="pspp.pdf"
+   output-type=pdf|ps|png|svg
+   paper-size=letter (see "papersize" file)
+   orientation=portrait|landscape
+   headers=on|off
+
+   left-margin=0.5in
+   right-margin=0.5in
+   top-margin=0.5in
+   bottom-margin=0.5in
+
+   prop-font=serif
+   emph-font=serif italic
+   fixed-font=monospace
+   font-size=10000
+
+   line-gutter=1pt
+   line-spacing=1pt
+   line-width=0.5pt
+ */
+
+/* Measurements as we present to the rest of PSPP. */
+#define XR_POINT PANGO_SCALE
+#define XR_INCH (XR_POINT * 72)
+
+/* Conversions to and from points. */
+static double
+xr_to_pt (int x)
+{
+  return x / (double) XR_POINT;
+}
+
+static int
+pt_to_xr (double x)
+{
+  return x * XR_POINT + 0.5;
+}
+
+/* Output types. */
+enum xr_output_type
+  {
+    XR_PDF,
+    XR_PS,
+    XR_SVG
+  };
+
+/* A font for use with Cairo. */
+struct xr_font
+  {
+    char *string;
+    PangoFontDescription *desc;
+    PangoLayout *layout;
+    PangoFontMetrics *metrics;
+  };
+
+/* Cairo output driver extension record. */
+struct xr_driver_ext
+  {
+    cairo_t *cairo;
+    struct xr_font fonts[OUTP_FONT_CNT];
+
+    bool draw_headers;          /* Draw headers at top of page? */
+    int page_number;           /* Current page number. */
+
+    int line_gutter;           /* Space around lines. */
+    int line_space;            /* Space between lines. */
+    int line_width;            /* Width of lines. */
+  };
+
+struct xr_driver_options
+  {
+    struct outp_driver *driver;
+
+    char *file_name;            /* Output file name. */
+    enum xr_output_type file_type; /* Type of output file. */
+
+
+    bool portrait;              /* Portrait mode? */
+
+    int paper_width;            /* Width of paper before dropping margins. */
+    int paper_length;           /* Length of paper before dropping margins. */
+    int left_margin;           /* Left margin in XR units. */
+    int right_margin;          /* Right margin in XR units. */
+    int top_margin;            /* Top margin in XR units. */
+    int bottom_margin;         /* Bottom margin in XR units. */
+  };
+
+static bool handle_option (void *options, const char *key,
+                           const struct string *val);
+static void draw_headers (struct outp_driver *this);
+
+static bool load_font (struct outp_driver *this, struct xr_font *);
+static void free_font (struct xr_font *);
+static int text_width (struct outp_driver *, const char *, enum outp_font);
+\f
+/* Driver initialization. */
+
+static struct outp_driver *
+xr_allocate (const char *name, int types)
+{
+  struct outp_driver *this;
+  struct xr_driver_ext *x;
+  size_t i;
+
+  this = outp_allocate_driver (&cairo_class, name, types);
+  this->width = this->length = 0;
+  this->font_height = XR_POINT * 10;
+  this->ext = x = xzalloc (sizeof *x);
+  x->cairo = NULL;
+  x->fonts[OUTP_FIXED].string = xstrdup ("monospace");
+  x->fonts[OUTP_PROPORTIONAL].string = xstrdup ("serif");
+  x->fonts[OUTP_EMPHASIS].string = xstrdup ("serif italic");
+  for (i = 0; i < OUTP_FONT_CNT; i++)
+    {
+      struct xr_font *font = &x->fonts[i];
+      font->desc = NULL;
+      font->metrics = NULL;
+      font->layout = NULL;
+    }
+  x->draw_headers = true;
+  x->page_number = 0;
+  x->line_gutter = XR_POINT;
+  x->line_space = XR_POINT;
+  x->line_width = XR_POINT / 2;
+
+  return this;
+}
+
+static bool
+xr_set_cairo (struct outp_driver *this, cairo_t *cairo)
+{
+  struct xr_driver_ext *x = this->ext;
+  int i;
+
+  x->cairo = cairo;
+
+  cairo_set_line_width (x->cairo, xr_to_pt (x->line_width));
+
+  for (i = 0; i < OUTP_FONT_CNT; i++)
+    if (!load_font (this, &x->fonts[i]))
+      return false;
+
+  this->fixed_width = text_width (this, "0", OUTP_FIXED);
+  this->prop_em_width = text_width (this, "0", OUTP_PROPORTIONAL);
+
+  this->horiz_line_width[OUTP_L_NONE] = 0;
+  this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
+  this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
+                                           + 2 * x->line_width);
+  memcpy (this->vert_line_width, this->horiz_line_width,
+          sizeof this->vert_line_width);
+
+  return true;
+}
+
+struct outp_driver *
+xr_create_driver (cairo_t *cairo)
+{
+  struct outp_driver *this;
+
+  this = xr_allocate ("cairo", 0);
+  this->width = INT_MAX / 8;
+  this->length = INT_MAX / 8;
+  if (!xr_set_cairo (this, cairo))
+    {
+      this->class->close_driver (this);
+      outp_free_driver (this);
+      return NULL;
+    }
+  return this;
+}
+
+static bool
+xr_open_driver (const char *name, int types, struct substring option_string)
+{
+  struct outp_driver *this;
+  struct xr_driver_ext *x;
+  struct xr_driver_options options;
+  cairo_surface_t *surface;
+  cairo_status_t status;
+  double width_pt, length_pt;
+
+  this = xr_allocate (name, types);
+  x = this->ext;
+
+  options.driver = this;
+  options.file_name = xstrdup ("pspp.pdf");
+  options.file_type = XR_PDF;
+  options.portrait = true;
+  outp_get_paper_size ("", &options.paper_width, &options.paper_length);
+  options.left_margin = XR_INCH / 2;
+  options.right_margin = XR_INCH / 2;
+  options.top_margin = XR_INCH / 2;
+  options.bottom_margin = XR_INCH / 2;
+
+  outp_parse_options (this->name, option_string, handle_option, &options);
+
+  width_pt = options.paper_width / 1000.0;
+  length_pt = options.paper_length / 1000.0;
+  if (options.portrait)
+    {
+      this->width = pt_to_xr (width_pt);
+      this->length = pt_to_xr (length_pt);
+    }
+  else
+    {
+      this->width = pt_to_xr (width_pt);
+      this->length = pt_to_xr (length_pt);
+    }
+  if (x->draw_headers)
+    options.top_margin += 3 * this->font_height;
+  this->width -= options.left_margin + options.right_margin;
+  this->length -= options.top_margin + options.bottom_margin;
+
+  if (options.file_type == XR_PDF)
+    surface = cairo_pdf_surface_create (options.file_name,
+                                        width_pt, length_pt);
+  else if (options.file_type == XR_PS)
+    surface = cairo_ps_surface_create (options.file_name, width_pt, length_pt);
+  else if (options.file_type == XR_SVG)
+    surface = cairo_svg_surface_create (options.file_name,
+                                        width_pt, length_pt);
+  else
+    NOT_REACHED ();
+
+  status = cairo_surface_status (surface);
+  if (status != CAIRO_STATUS_SUCCESS)
+    {
+      error (0, 0, _("opening output file \"%s\": %s"),
+             options.file_name, cairo_status_to_string (status));
+      cairo_surface_destroy (surface);
+      goto error;
+    }
+
+  x->cairo = cairo_create (surface);
+  cairo_surface_destroy (surface);
+
+  cairo_translate (x->cairo,
+                   xr_to_pt (options.left_margin),
+                   xr_to_pt (options.top_margin));
+
+  if (this->length / this->font_height < 15)
+    {
+      error (0, 0, _("The defined page is not long "
+                     "enough to hold margins and headers, plus least 15 "
+                     "lines of the default fonts.  In fact, there's only "
+                     "room for %d lines."),
+             this->length / this->font_height);
+      goto error;
+    }
+
+  if (!xr_set_cairo (this, x->cairo))
+    goto error;
+
+  outp_register_driver (this);
+  free (options.file_name);
+  return true;
+
+ error:
+  this->class->close_driver (this);
+  outp_free_driver (this);
+  free (options.file_name);
+  return false;
+}
+
+static bool
+xr_close_driver (struct outp_driver *this)
+{
+  struct xr_driver_ext *x = this->ext;
+  bool ok = true;
+  size_t i;
+
+  if (x->cairo != NULL)
+    {
+      cairo_status_t status;
+
+      cairo_surface_finish (cairo_get_target (x->cairo));
+      status = cairo_status (x->cairo);
+      if (status != CAIRO_STATUS_SUCCESS)
+        error (0, 0, _("error writing output file for %s driver: %s"),
+               this->name, cairo_status_to_string (status));
+      cairo_destroy (x->cairo);
+    }
+
+  for (i = 0; i < OUTP_FONT_CNT; i++)
+    free_font (&x->fonts[i]);
+  free (x);
+
+  return ok;
+}
+
+/* Generic option types. */
+enum
+{
+  output_file_arg,
+  output_type_arg,
+  paper_size_arg,
+  orientation_arg,
+  line_style_arg,
+  boolean_arg,
+  dimension_arg,
+  string_arg,
+  nonneg_int_arg
+};
+
+/* All the options that the Cairo driver supports. */
+static const struct outp_option option_tab[] =
+{
+  {"output-file",              output_file_arg,0},
+  {"output-type",               output_type_arg,0},
+  {"paper-size",               paper_size_arg, 0},
+  {"orientation",              orientation_arg,0},
+
+  {"headers",                  boolean_arg,    1},
+
+  {"prop-font",                string_arg,     OUTP_PROPORTIONAL},
+  {"emph-font",                string_arg,     OUTP_EMPHASIS},
+  {"fixed-font",               string_arg,     OUTP_FIXED},
+
+  {"left-margin",              dimension_arg,  0},
+  {"right-margin",             dimension_arg,  1},
+  {"top-margin",               dimension_arg,  2},
+  {"bottom-margin",            dimension_arg,  3},
+  {"font-size",                        dimension_arg,  4},
+  {"line-width",               dimension_arg,  5},
+  {"line-gutter",              dimension_arg,  6},
+  {"line-width",               dimension_arg,  7},
+  {NULL, 0, 0},
+};
+
+static bool
+handle_option (void *options_, const char *key, const struct string *val)
+{
+  struct xr_driver_options *options = options_;
+  struct outp_driver *this = options->driver;
+  struct xr_driver_ext *x = this->ext;
+  int subcat;
+  char *value = ds_cstr (val);
+
+  switch (outp_match_keyword (key, option_tab, &subcat))
+    {
+    case -1:
+      error (0, 0,
+             _("unknown configuration parameter `%s' for %s device "
+               "driver"), key, this->class->name);
+      break;
+    case output_file_arg:
+      free (options->file_name);
+      options->file_name = xstrdup (value);
+      break;
+    case output_type_arg:
+      if (!strcmp (value, "pdf"))
+        options->file_type = XR_PDF;
+      else if (!strcmp (value, "ps"))
+        options->file_type = XR_PS;
+      else if (!strcmp (value, "svg"))
+        options->file_type = XR_SVG;
+      else
+        {
+          error (0, 0, _("unknown Cairo output type \"%s\""), value);
+          return false;
+        }
+      break;
+    case paper_size_arg:
+      outp_get_paper_size (value,
+                           &options->paper_width, &options->paper_length);
+      break;
+    case orientation_arg:
+      if (!strcmp (value, "portrait"))
+       options->portrait = true;
+      else if (!strcmp (value, "landscape"))
+       options->portrait = false;
+      else
+       error (0, 0, _("unknown orientation `%s' (valid orientations are "
+                       "`portrait' and `landscape')"), value);
+      break;
+    case boolean_arg:
+      if (!strcmp (value, "on") || !strcmp (value, "true")
+          || !strcmp (value, "yes") || atoi (value))
+        x->draw_headers = true;
+      else if (!strcmp (value, "off") || !strcmp (value, "false")
+               || !strcmp (value, "no") || !strcmp (value, "0"))
+        x->draw_headers = false;
+      else
+        {
+          error (0, 0, _("boolean value expected for %s"), key);
+          return false;
+        }
+      break;
+    case dimension_arg:
+      {
+       int dimension = outp_evaluate_dimension (value);
+
+       if (dimension <= 0)
+          break;
+       switch (subcat)
+         {
+         case 0:
+           options->left_margin = dimension;
+           break;
+         case 1:
+           options->right_margin = dimension;
+           break;
+         case 2:
+           options->top_margin = dimension;
+           break;
+         case 3:
+           options->bottom_margin = dimension;
+           break;
+         case 4:
+           this->font_height = dimension;
+           break;
+         case 5:
+           x->line_width = dimension;
+           break;
+         case 6:
+           x->line_gutter = dimension;
+           break;
+         case 7:
+           x->line_width = dimension;
+           break;
+         default:
+           NOT_REACHED ();
+         }
+      }
+      break;
+    case string_arg:
+      free (x->fonts[subcat].string);
+      x->fonts[subcat].string = ds_xstrdup (val);
+      break;
+    default:
+      NOT_REACHED ();
+    }
+
+  return true;
+}
+\f
+/* Basic file operations. */
+
+static void
+xr_open_page (struct outp_driver *this)
+{
+  struct xr_driver_ext *x = this->ext;
+
+  x->page_number++;
+
+  if (x->draw_headers)
+    draw_headers (this);
+}
+
+static void
+xr_close_page (struct outp_driver *this)
+{
+  struct xr_driver_ext *x = this->ext;
+  cairo_show_page (x->cairo);
+}
+
+static void
+xr_output_chart (struct outp_driver *this, const struct chart *chart)
+{
+  struct xr_driver_ext *x = this->ext;
+  struct chart_geometry geom;
+
+  outp_eject_page (this);
+  outp_open_page (this);
+
+  cairo_save (x->cairo);
+  cairo_translate (x->cairo, 0.0, xr_to_pt (this->length));
+  cairo_scale (x->cairo, 1.0, -1.0);
+  chart_geometry_init (x->cairo, &geom,
+                       xr_to_pt (this->width), xr_to_pt (this->length));
+  chart_draw (chart, x->cairo, &geom);
+  chart_geometry_free (x->cairo, &geom);
+  cairo_restore (x->cairo);
+
+  outp_close_page (this);
+}
+\f
+/* Draws a line from (x0,y0) to (x1,y1). */
+static void
+dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
+{
+  struct xr_driver_ext *x = this->ext;
+  cairo_new_path (x->cairo);
+  cairo_move_to (x->cairo, xr_to_pt (x0), xr_to_pt (y0));
+  cairo_line_to (x->cairo, xr_to_pt (x1), xr_to_pt (y1));
+  cairo_stroke (x->cairo);
+}
+
+/* Draws a horizontal line X0...X2 at Y if LEFT says so,
+   shortening it to X0...X1 if SHORTEN is true.
+   Draws a horizontal line X1...X3 at Y if RIGHT says so,
+   shortening it to X2...X3 if SHORTEN is true. */
+static void
+horz_line (struct outp_driver *this,
+           int x0, int x1, int x2, int x3, int y,
+           enum outp_line_style left, enum outp_line_style right,
+           bool shorten)
+{
+  if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
+    dump_line (this, x0, y, x3, y);
+  else
+    {
+      if (left != OUTP_L_NONE)
+        dump_line (this, x0, y, shorten ? x1 : x2, y);
+      if (right != OUTP_L_NONE)
+        dump_line (this, shorten ? x2 : x1, y, x3, y);
+    }
+}
+
+/* Draws a vertical line Y0...Y2 at X if TOP says so,
+   shortening it to Y0...Y1 if SHORTEN is true.
+   Draws a vertical line Y1...Y3 at X if BOTTOM says so,
+   shortening it to Y2...Y3 if SHORTEN is true. */
+static void
+vert_line (struct outp_driver *this,
+           int y0, int y1, int y2, int y3, int x,
+           enum outp_line_style top, enum outp_line_style bottom,
+           bool shorten)
+{
+  if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
+    dump_line (this, x, y0, x, y3);
+  else
+    {
+      if (top != OUTP_L_NONE)
+        dump_line (this, x, y0, x, shorten ? y1 : y2);
+      if (bottom != OUTP_L_NONE)
+        dump_line (this, x, shorten ? y2 : y1, x, y3);
+    }
+}
+
+/* Draws a generalized intersection of lines in the rectangle
+   (X0,Y0)-(X3,Y3).  The line coming from the top to the center
+   is of style TOP, from left to center of style LEFT, from
+   bottom to center of style BOTTOM, and from right to center of
+   style RIGHT. */
+static void
+xr_line (struct outp_driver *this,
+         int x0, int y0, int x3, int y3,
+         enum outp_line_style top, enum outp_line_style left,
+         enum outp_line_style bottom, enum outp_line_style right)
+{
+  /* The algorithm here is somewhat subtle, to allow it to handle
+     all the kinds of intersections that we need.
+
+     Three additional ordinates are assigned along the x axis.  The
+     first is xc, midway between x0 and x3.  The others are x1 and
+     x2; for a single vertical line these are equal to xc, and for
+     a double vertical line they are the ordinates of the left and
+     right half of the double line.
+
+     yc, y1, and y2 are assigned similarly along the y axis.
+
+     The following diagram shows the coordinate system and output
+     for double top and bottom lines, single left line, and no
+     right line:
+
+                 x0       x1 xc  x2      x3
+               y0 ________________________
+                  |        #     #       |
+                  |        #     #       |
+                  |        #     #       |
+                  |        #     #       |
+                  |        #     #       |
+     y1 = y2 = yc |#########     #       |
+                  |        #     #       |
+                  |        #     #       |
+                  |        #     #       |
+                  |        #     #       |
+               y3 |________#_____#_______|
+  */
+  struct xr_driver_ext *ext = this->ext;
+
+  /* Offset from center of each line in a pair of double lines. */
+  int double_line_ofs = (ext->line_space + ext->line_width) / 2;
+
+  /* Are the lines along each axis single or double?
+     (It doesn't make sense to have different kinds of line on the
+     same axis, so we don't try to gracefully handle that case.) */
+  bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
+  bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
+
+  /* When horizontal lines are doubled,
+     the left-side line along y1 normally runs from x0 to x2,
+     and the right-side line along y1 from x3 to x1.
+     If the top-side line is also doubled, we shorten the y1 lines,
+     so that the left-side line runs only to x1,
+     and the right-side line only to x2.
+     Otherwise, the horizontal line at y = y1 below would cut off
+     the intersection, which looks ugly:
+               x0       x1     x2      x3
+             y0 ________________________
+                |        #     #       |
+                |        #     #       |
+                |        #     #       |
+                |        #     #       |
+             y1 |#########     ########|
+                |                      |
+                |                      |
+             y2 |######################|
+                |                      |
+                |                      |
+             y3 |______________________|
+     It is more of a judgment call when the horizontal line is
+     single.  We actually choose to cut off the line anyhow, as
+     shown in the first diagram above.
+  */
+  bool shorten_y1_lines = top == OUTP_L_DOUBLE;
+  bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
+  bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
+  int horz_line_ofs = double_vert ? double_line_ofs : 0;
+  int xc = (x0 + x3) / 2;
+  int x1 = xc - horz_line_ofs;
+  int x2 = xc + horz_line_ofs;
+
+  bool shorten_x1_lines = left == OUTP_L_DOUBLE;
+  bool shorten_x2_lines = right == OUTP_L_DOUBLE;
+  bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
+  int vert_line_ofs = double_horz ? double_line_ofs : 0;
+  int yc = (y0 + y3) / 2;
+  int y1 = yc - vert_line_ofs;
+  int y2 = yc + vert_line_ofs;
+
+  if (!double_horz)
+    horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
+  else
+    {
+      horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
+      horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
+    }
+
+  if (!double_vert)
+    vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
+  else
+    {
+      vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
+      vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
+    }
+}
+
+/* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
+   and with the given JUSTIFICATION for THIS driver. */
+static int
+draw_text (struct outp_driver *this,
+           const char *string, int x, int y, int max_width,
+           enum outp_justification justification)
+{
+  struct outp_text text;
+  int width;
+
+  text.font = OUTP_PROPORTIONAL;
+  text.justification = justification;
+  text.string = ss_cstr (string);
+  text.h = max_width;
+  text.v = this->font_height;
+  text.x = x;
+  text.y = y;
+  this->class->text_metrics (this, &text, &width, NULL);
+  this->class->text_draw (this, &text);
+  return width;
+}
+
+/* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
+   and with the given JUSTIFICATION for THIS driver. */
+static int
+text_width (struct outp_driver *this, const char *string, enum outp_font font)
+{
+  struct outp_text text;
+  int width;
+
+  text.font = font;
+  text.justification = OUTP_LEFT;
+  text.string = ss_cstr (string);
+  text.h = INT_MAX;
+  text.v = this->font_height;
+  text.x = 0;
+  text.y = 0;
+  this->class->text_metrics (this, &text, &width, NULL);
+  return width;
+}
+
+/* Writes LEFT left-justified and RIGHT right-justified within
+   (X0...X1) at Y.  LEFT or RIGHT or both may be null. */
+static void
+draw_header_line (struct outp_driver *this,
+                  const char *left, const char *right,
+                  int x0, int x1, int y)
+{
+  int right_width = 0;
+  if (right != NULL)
+    right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
+                   + this->prop_em_width);
+  if (left != NULL)
+    draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
+}
+
+/* Draw top of page headers for THIS driver. */
+static void
+draw_headers (struct outp_driver *this)
+{
+  struct xr_driver_ext *ext = this->ext;
+  char *r1, *r2;
+  int x0, x1;
+  int y;
+
+  y = -3 * this->font_height;
+  x0 = this->prop_em_width;
+  x1 = this->width - this->prop_em_width;
+
+  /* Draw box. */
+  cairo_rectangle (ext->cairo, 0, xr_to_pt (y), xr_to_pt (this->width),
+                   xr_to_pt (2 * (this->font_height
+                                  + ext->line_width + ext->line_gutter)));
+  cairo_save (ext->cairo);
+  cairo_set_source_rgb (ext->cairo, 0.9, 0.9, 0.9);
+  cairo_fill_preserve (ext->cairo);
+  cairo_restore (ext->cairo);
+  cairo_stroke (ext->cairo);
+
+  y += ext->line_width + ext->line_gutter;
+
+  r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
+  r2 = xasprintf ("%s - %s", version, host_system);
+
+  draw_header_line (this, outp_title, r1, x0, x1, y);
+  y += this->font_height;
+
+  draw_header_line (this, outp_subtitle, r2, x0, x1, y);
+
+  free (r1);
+  free (r2);
+}
+\f
+/* Format TEXT on THIS driver.
+   If DRAW is nonzero, draw the text.
+   The width of the widest line is stored into *WIDTH, if WIDTH
+   is nonnull.
+   The total height of the text written is stored into *HEIGHT,
+   if HEIGHT is nonnull. */
+static void
+text (struct outp_driver *this, const struct outp_text *text, bool draw,
+      int *width, int *height)
+{
+  struct xr_driver_ext *ext = this->ext;
+  struct xr_font *font = &ext->fonts[text->font];
+
+  pango_layout_set_text (font->layout,
+                         text->string.string, text->string.length);
+  pango_layout_set_alignment (
+    font->layout,
+    (text->justification == OUTP_RIGHT ? PANGO_ALIGN_RIGHT
+     : text->justification == OUTP_LEFT ? PANGO_ALIGN_LEFT
+     : PANGO_ALIGN_CENTER));
+  pango_layout_set_width (font->layout, text->h == INT_MAX ? -1 : text->h);
+  pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD_CHAR);
+  /* XXX need to limit number of lines to those that fit in text->v. */
+
+  if (draw)
+    {
+      int x = text->x;
+      if (text->justification != OUTP_LEFT && text->h != INT_MAX)
+        {
+          int w, h, excess;
+          pango_layout_get_size (font->layout, &w, &h);
+          excess = text->h - w;
+          if (excess > 0)
+            {
+              if (text->justification == OUTP_CENTER)
+                x += excess / 2;
+              else
+                x += excess;
+            }
+        }
+      cairo_save (ext->cairo);
+      cairo_translate (ext->cairo, xr_to_pt (text->x), xr_to_pt (text->y));
+      pango_cairo_show_layout (ext->cairo, font->layout);
+      cairo_restore (ext->cairo);
+    }
+
+  if (width != NULL || height != NULL)
+    {
+      int w, h;
+      pango_layout_get_size (font->layout, &w, &h);
+      if (width != NULL)
+        *width = w;
+      if (height != NULL)
+        *height = h;
+    }
+}
+
+static void
+xr_text_metrics (struct outp_driver *this, const struct outp_text *t,
+                 int *width, int *height)
+{
+  text (this, t, false, width, height);
+}
+
+static void
+xr_text_draw (struct outp_driver *this, const struct outp_text *t)
+{
+  text (this, t, true, NULL, NULL);
+}
+\f
+/* Attempts to load FONT, initializing its other members based on
+   its 'string' member and the information in THIS.  Returns true
+   if successful, otherwise false. */
+static bool
+load_font (struct outp_driver *this, struct xr_font *font)
+{
+  struct xr_driver_ext *x = this->ext;
+  PangoContext *context;
+  PangoLanguage *language;
+
+  font->desc = pango_font_description_from_string (font->string);
+  if (font->desc == NULL)
+    {
+      error (0, 0, _("\"%s\": bad font specification"), font->string);
+      return false;
+    }
+  pango_font_description_set_absolute_size (font->desc, this->font_height);
+
+  font->layout = pango_cairo_create_layout (x->cairo);
+  pango_layout_set_font_description (font->layout, font->desc);
+
+  language = pango_language_get_default ();
+  context = pango_layout_get_context (font->layout);
+  font->metrics = pango_context_get_metrics (context, font->desc, language);
+
+  return true;
+}
+
+/* Frees FONT. */
+static void
+free_font (struct xr_font *font)
+{
+  free (font->string);
+  if (font->desc != NULL)
+    pango_font_description_free (font->desc);
+  pango_font_metrics_unref (font->metrics);
+  g_object_unref (font->layout);
+}
+
+/* Cairo driver class. */
+const struct outp_class cairo_class =
+{
+  "cairo",
+  0,
+
+  xr_open_driver,
+  xr_close_driver,
+
+  xr_open_page,
+  xr_close_page,
+  NULL,
+
+  xr_output_chart,
+
+  NULL,
+
+  xr_line,
+  xr_text_metrics,
+  xr_text_draw,
+};
diff --git a/src/output/cairo.h b/src/output/cairo.h
new file mode 100644 (file)
index 0000000..c4f8a81
--- /dev/null
@@ -0,0 +1,24 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CAIRO_H
+#define OUTPUT_CAIRO_H 1
+
+#include <cairo/cairo.h>
+
+struct outp_driver *xr_create_driver (cairo_t *);
+
+#endif /* output/cairo.h */
diff --git a/src/output/chart-provider.h b/src/output/chart-provider.h
new file mode 100644 (file)
index 0000000..9becb6f
--- /dev/null
@@ -0,0 +1,89 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2004, 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CHART_PROVIDER_H
+#define OUTPUT_CHART_PROVIDER_H 1
+
+#include <cairo/cairo.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <output/chart.h>
+
+struct chart_colour
+  {
+    uint8_t red;
+    uint8_t green;
+    uint8_t blue;
+  };
+
+/* The geometry of a chart. */
+struct chart_geometry
+  {
+    int data_top   ;
+    int data_right ;
+    int data_bottom;
+    int data_left  ;
+
+    int abscissa_top;
+
+    int ordinate_right ;
+
+    int title_bottom ;
+
+    /* Legend. */
+    int legend_left ;
+    int legend_right ;
+    const char **dataset;
+    int n_datasets;
+
+    /* Default font size for the plot. */
+    double font_size;
+
+    struct chart_colour fill_colour;
+
+    /* Stuff Particular to Cartesians (and Boxplots ) */
+    double ordinate_scale;
+    double abscissa_scale;
+    double x_min;
+    double x_max;
+    double y_min;
+    double y_max;
+    bool in_path;
+  };
+
+struct chart_class
+  {
+    void (*draw) (const struct chart *, cairo_t *, struct chart_geometry *);
+    void (*destroy) (struct chart *);
+  };
+
+struct chart
+  {
+    const struct chart_class *class;
+    int ref_cnt;
+  };
+
+void chart_init (struct chart *, const struct chart_class *);
+
+void chart_geometry_init (cairo_t *, struct chart_geometry *,
+                          double width, double length);
+void chart_geometry_free (cairo_t *, struct chart_geometry *);
+
+void chart_draw (const struct chart *, cairo_t *, struct chart_geometry *);
+char *chart_draw_png (const struct chart *, const char *file_name_template,
+                      int number);
+
+#endif /* output/chart-provider.h */
index e324901cc4df1d445f3fc8c7a4b64296bca55bca..e31422bc6bb35799243d68337d41914ae059c430 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 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
 #include <config.h>
 
 #include <output/chart.h>
+#include <output/chart-provider.h>
 
 #include <assert.h>
+#include <cairo/cairo.h>
 #include <errno.h>
 #include <float.h>
 #include <math.h>
@@ -27,8 +29,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <plot.h>
-
 #include <libpspp/str.h>
 #include <output/manager.h>
 #include <output/output.h>
 
 extern struct som_table_class tab_table_class;
 
-struct chart *
-chart_create(void)
+void
+chart_init (struct chart *chart, const struct chart_class *class)
 {
-  struct chart *chart;
-  struct outp_driver *d;
-
-  d = outp_drivers (NULL);
-  if (d == NULL)
-    return NULL;
-
-  chart = xmalloc (sizeof *chart);
-  chart->lp = NULL;
-  d->class->initialise_chart(d, chart);
-  if (!chart->lp)
-    {
-      free (chart);
-      return NULL;
-    }
-
-  if (pl_openpl_r (chart->lp) < 0)      /* open Plotter */
-    return NULL;
-
-  pl_fspace_r (chart->lp, 0.0, 0.0, 1000.0, 1000.0); /* set coordinate system */
-  pl_flinewidth_r (chart->lp, 0.25);    /* set line thickness */
-  pl_pencolorname_r (chart->lp, "black");
-
-  pl_erase_r (chart->lp);               /* erase graphics display */
-  pl_filltype_r(chart->lp,0);
-
-  pl_savestate_r(chart->lp);
-
-  /* Set default chartetry */
-  chart->data_top =   900;
-  chart->data_right = 800;
-  chart->data_bottom = 120;
-  chart->data_left = 150;
-  chart->abscissa_top = 70;
-  chart->ordinate_right = 120;
-  chart->title_bottom = 920;
-  chart->legend_left = 810;
-  chart->legend_right = 1000;
-  chart->font_size = 0;
-  chart->in_path = false;
-  chart->dataset = NULL;
-  chart->n_datasets = 0;
-  strcpy(chart->fill_colour,"red");
-
-  /* Get default font size */
-  if ( !chart->font_size)
-    chart->font_size = pl_fontsize_r(chart->lp, -1);
-
-  /* Draw the data area */
-  pl_box_r(chart->lp,
-          chart->data_left, chart->data_bottom,
-          chart->data_right, chart->data_top);
+  chart->class = class;
+  chart->ref_cnt = 1;
+}
 
-  return chart;
+void
+chart_geometry_init (cairo_t *cr, struct chart_geometry *geom,
+                     double width, double length)
+{
+  /* Set default chartetry. */
+  geom->data_top = 0.900 * length;
+  geom->data_right = 0.800 * width;
+  geom->data_bottom = 0.120 * length;
+  geom->data_left = 0.150 * width;
+  geom->abscissa_top = 0.070 * length;
+  geom->ordinate_right = 0.120 * width;
+  geom->title_bottom = 0.920 * length;
+  geom->legend_left = 0.810 * width;
+  geom->legend_right = width;
+  geom->font_size = 15.0;
+  geom->in_path = false;
+  geom->dataset = NULL;
+  geom->n_datasets = 0;
+
+  geom->fill_colour.red = 255;
+  geom->fill_colour.green = 0;
+  geom->fill_colour.blue = 0;
+
+  cairo_set_line_width (cr, 1.0);
+
+  cairo_rectangle (cr, geom->data_left, geom->data_bottom,
+                   geom->data_right - geom->data_left,
+                   geom->data_top - geom->data_bottom);
+  cairo_stroke (cr);
 }
 
 void
-chart_submit(struct chart *chart)
+chart_geometry_free (cairo_t *cr UNUSED, struct chart_geometry *geom)
 {
   int i;
-  struct som_entity s;
-  struct outp_driver *d;
-
-  if ( ! chart )
-     return ;
-
-  pl_restorestate_r(chart->lp);
-
-  s.class = &tab_table_class;
-  s.ext = chart;
-  s.type = SOM_CHART;
-  som_submit (&s);
 
-  if (pl_closepl_r (chart->lp) < 0)     /* close Plotter */
-    {
-      fprintf (stderr, "Couldn't close Plotter\n");
-    }
-
-  pl_deletepl_r(chart->lp);
+  for (i = 0 ; i < geom->n_datasets; ++i)
+    free (geom->dataset[i]);
+  free (geom->dataset);
+}
 
-  pl_deleteplparams(chart->pl_params);
+void
+chart_draw (const struct chart *chart, cairo_t *cr,
+            struct chart_geometry *geom)
+{
+  chart->class->draw (chart, cr, geom);
+}
 
-  d = outp_drivers (NULL);
-  d->class->finalise_chart(d, chart);
+char *
+chart_draw_png (const struct chart *chart, const char *file_name_template,
+                int number)
+{
+  const int width = 640;
+  const int length = 480;
+
+  struct chart_geometry geom;
+  cairo_surface_t *surface;
+  cairo_status_t status;
+  const char *number_pos;
+  char *file_name;
+  cairo_t *cr;
+
+  number_pos = strchr (file_name_template, '#');
+  if (number_pos != NULL)
+    file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
+                           file_name_template, number, number_pos + 1);
+  else
+    file_name = xstrdup (file_name_template);
+
+  surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
+  cr = cairo_create (surface);
+
+  cairo_translate (cr, 0.0, length);
+  cairo_scale (cr, 1.0, -1.0);
+
+  cairo_save (cr);
+  cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+  cairo_rectangle (cr, 0, 0, width, length);
+  cairo_fill (cr);
+  cairo_restore (cr);
+
+  cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+
+  chart_geometry_init (cr, &geom, width, length);
+  chart_draw (chart, cr, &geom);
+  chart_geometry_free (cr, &geom);
+
+  status = cairo_surface_write_to_png (surface, file_name);
+  if (status != CAIRO_STATUS_SUCCESS)
+    error (0, 0, _("writing output file \"%s\": %s"),
+           file_name, cairo_status_to_string (status));
+
+  cairo_destroy (cr);
+  cairo_surface_destroy (surface);
+
+  return file_name;
+}
 
-  for (i = 0 ; i < chart->n_datasets; ++i)
-    free (chart->dataset[i]);
-  free (chart->dataset);
 
-  free(chart);
+struct chart *
+chart_ref (const struct chart *chart_)
+{
+  struct chart *chart = CONST_CAST (struct chart *, chart_);
+  chart->ref_cnt++;
+  return chart;
 }
 
 void
-chart_init_separate (struct chart *ch, const char *type,
-                     const char *file_name_tmpl, int number)
+chart_unref (struct chart *chart)
 {
-  FILE *fp;
-  int number_pos;
-
-  number_pos = strchr (file_name_tmpl, '#') - file_name_tmpl;
-  ch->file_name = xasprintf ("%.*s%d%s",
-                             number_pos, file_name_tmpl,
-                             number,
-                             file_name_tmpl + number_pos + 1);
-  fp = fopen (ch->file_name, "wb");
-  if (fp == NULL)
+  if (chart != NULL)
     {
-      error (0, errno, _("creating \"%s\""), ch->file_name);
-      free (ch->file_name);
-      ch->file_name = NULL;
-      return;
+      assert (chart->ref_cnt > 0);
+      if (--chart->ref_cnt == 0)
+        chart->class->destroy (chart);
     }
-
-  ch->pl_params = pl_newplparams ();
-  ch->lp = pl_newpl_r (type, 0, fp, stderr, ch->pl_params);
 }
 
 void
-chart_finalise_separate (struct chart *ch)
+chart_submit (struct chart *chart)
 {
-  free (ch->file_name);
+#ifdef HAVE_CAIRO
+  struct outp_driver *d;
+
+  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
+    if (d->class->output_chart != NULL)
+      d->class->output_chart (d, chart);
+#endif
+
+  chart_unref (chart);
 }
index 4da12ecfe5b0afba3ea7035680fca5b1ea1d9af9..d6b9b3d49e38222ecd8ba36ce49c87f69037003a 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 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
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdio.h>
-#include <float.h>
-#include <assert.h>
-#include <math.h>
+#ifndef OUTPUT_CHART_H
+#define OUTPUT_CHART_H 1
 
-#include <math/chart-geometry.h>
-#include <libpspp/str.h>
-#include "manager.h"
-#include "output.h"
+#include <cairo/cairo.h>
 
-#include "xalloc.h"
+struct chart;
 
-#ifndef CHART_H
-#define CHART_H
+struct chart *chart_ref (const struct chart *);
+void chart_unref (struct chart *);
 
-#ifndef NO_CHARTS
-#include <plot.h>
-#endif
+void chart_submit (struct chart *);
 
-struct chart {
-
-#ifndef NO_CHARTS
-  plPlotter *lp ;
-  plPlotterParams *pl_params;
-#else
-  void *lp;
-#endif
-  char *file_name;
-  FILE *file;
-
-  /* The geometry of the chart
-     See diagram at the foot of this file.
-   */
-
-  int data_top   ;
-  int data_right ;
-  int data_bottom;
-  int data_left  ;
-
-  int abscissa_top;
-
-  int ordinate_right ;
-
-  int title_bottom ;
-
-  int legend_left ;
-  int legend_right ;
-  const char **dataset;
-  int n_datasets;
-
-
-  /* Default font size for the plot (if zero, then use plotter default) */
-  int font_size;
-
-  char fill_colour[10];
-
-  /* Stuff Particular to Cartesians (and Boxplots ) */
-  double ordinate_scale;
-  double abscissa_scale;
-  double x_min;
-  double x_max;
-  double y_min;
-  double y_max;
-  bool in_path;
-};
-
-
-
-struct chart * chart_create(void);
-void chart_submit(struct chart *ch);
-
-/* Helper functions for output drivers that put each chart into a
-   separate file. */
-void chart_init_separate (struct chart *, const char *type,
-                          const char *file_name_tmpl, int number);
-
-void chart_finalise_separate (struct chart *);
-
-#endif
+#endif /* output/chart.h */
diff --git a/src/output/charts/automake.mk b/src/output/charts/automake.mk
deleted file mode 100644 (file)
index ab0ff51..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-## Process this file with automake to produce Makefile.in  -*- makefile -*-
-
-noinst_LTLIBRARIES += src/output/charts/libcharts.la
-
-chart_sources = \
-       src/output/charts/barchart.c \
-       src/output/charts/barchart.h \
-       src/output/charts/box-whisker.c \
-       src/output/charts/box-whisker.h \
-       src/output/charts/cartesian.c \
-       src/output/charts/cartesian.h \
-       src/output/charts/piechart.c \
-       src/output/charts/piechart.h \
-       src/output/charts/plot-chart.h \
-       src/output/charts/plot-chart.c \
-       src/output/charts/plot-hist.c \
-       src/output/charts/plot-hist.h
-
-if WITHCHARTS
-src_output_charts_libcharts_la_SOURCES = \
-       $(chart_sources)
-
-EXTRA_DIST += src/output/charts/dummy-chart.c
-else
-src_output_charts_libcharts_la_SOURCES =  \
-       src/output/charts/dummy-chart.c
-
-EXTRA_DIST += $(chart_sources)
-
-AM_CPPFLAGS += -DNO_CHARTS
-
-endif
-
-EXTRA_DIST += src/output/charts/OChangeLog
diff --git a/src/output/charts/barchart.c b/src/output/charts/barchart.c
deleted file mode 100644 (file)
index f3b1001..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 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 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.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-
-#include <config.h>
-
-#include <stdio.h>
-#include <plot.h>
-#include <stdarg.h>
-#include <math.h>
-#include <output/charts/barchart.h>
-#include <output/chart.h>
-#include <output/charts/plot-chart.h>
-
-#define CATAGORIES 6
-#define SUB_CATAGORIES 3
-
-static const    double x_min = 0;
-static const    double x_max = 15.0;
-
-static const char *cat_labels[] =
-  {
-    "Age",
-    "Intelligence",
-    "Wealth",
-    "Emotional",
-    "cat 5",
-    "cat 6",
-    "cat 7",
-    "cat 8",
-    "cat 9",
-    "cat 10",
-    "cat 11"
-  };
-
-
-
-
-/* Subcatagories */
-static const double data1[] =
-{
-  28,83,
-  34,
-  29,13,
-   9,4,
-   3,3,
-   2,0,
-   1,0,
-   0,
-   1,1
-};
-
-
-static const double data2[] =
-{
-  45,13,
-   9,4,
-   3,43,
-   2,0,
-   1,20,
-   0,0,
-  1,1,
-  0,0
-};
-
-static const double data3[] =
-  {
-    23,18,
-    0, 45,23, 9, 40, 24,4, 8
-  };
-
-
-static const char subcat_name[]="Gender";
-
-
-struct subcat {
-  const double *data;
-  const char *label;
-};
-
-static const struct subcat sub_catagory[SUB_CATAGORIES] =
-  {
-    {data1, "male"},
-    {data2, "female"},
-    {data3, "47xxy"}
-  };
-
-
-
-static const    double y_min = 0;
-static const    double y_max = 120.0;
-static const    double y_tick = 20.0;
-
-
-
-static void write_legend(struct chart *chart) ;
-
-
-void
-draw_barchart(struct chart *ch, const char *title,
-             const char *xlabel, const char *ylabel, enum bar_opts opt)
-{
-  double d;
-  int i;
-
-  double interval_size = fabs(ch->data_right - ch->data_left) / ( CATAGORIES );
-
-  double bar_width = interval_size / 1.1 ;
-
-  double ordinate_scale = fabs(ch->data_top -  ch->data_bottom) /
-    fabs(y_max - y_min) ;
-
-  if ( opt != BAR_STACKED )
-      bar_width /= SUB_CATAGORIES;
-
-  /* Move to data bottom-left */
-  pl_move_r(ch->lp, ch->data_left, ch->data_bottom);
-
-  pl_savestate_r(ch->lp);
-  pl_filltype_r(ch->lp,1);
-
-  /* Draw the data */
-  for (i = 0 ; i < CATAGORIES ; ++i )
-    {
-      int sc;
-      double ystart=0.0;
-      double x = i * interval_size;
-
-      pl_savestate_r(ch->lp);
-
-      draw_tick (ch, TICK_ABSCISSA, x + (interval_size/2 ), "%s",
-                cat_labels[i]);
-
-      for(sc = 0 ; sc < SUB_CATAGORIES ; ++sc )
-       {
-
-         pl_savestate_r(ch->lp);
-         pl_fillcolorname_r(ch->lp,data_colour[sc % N_CHART_COLOURS]);
-
-         switch ( opt )
-           {
-           case BAR_GROUPED:
-             pl_fboxrel_r(ch->lp,
-                          x + (sc * bar_width ), 0,
-                          x + (sc + 1) * bar_width,
-                            sub_catagory[sc].data[i] * ordinate_scale );
-             break;
-
-
-           case BAR_STACKED:
-
-             pl_fboxrel_r(ch->lp,
-                          x, ystart,
-                          x + bar_width,
-                          ystart + sub_catagory[sc].data[i] * ordinate_scale );
-
-             ystart +=    sub_catagory[sc].data[i] * ordinate_scale ;
-
-             break;
-
-           default:
-             break;
-           }
-         pl_restorestate_r(ch->lp);
-       }
-
-      pl_restorestate_r(ch->lp);
-    }
-  pl_restorestate_r(ch->lp);
-
-  for ( d = y_min; d <= y_max ; d += y_tick )
-    {
-
-      draw_tick (ch, TICK_ORDINATE,
-                (d - y_min ) * ordinate_scale, "%g", d);
-
-    }
-
-  /* Write the abscissa label */
-  pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
-  pl_alabel_r(ch->lp,0,'t',xlabel);
-
-
-  /* Write the ordinate label */
-  pl_savestate_r(ch->lp);
-  pl_move_r(ch->lp,ch->data_bottom, ch->ordinate_right);
-  pl_textangle_r(ch->lp,90);
-  pl_alabel_r(ch->lp,0,0,ylabel);
-  pl_restorestate_r(ch->lp);
-
-
-  chart_write_title(ch, "%s", title);
-
-  write_legend(ch);
-
-
-}
-
-
-
-
-
-static void
-write_legend(struct chart *chart)
-{
-  int sc;
-
-  pl_savestate_r(chart->lp);
-
-  pl_filltype_r(chart->lp,1);
-
-  pl_move_r(chart->lp, chart->legend_left,
-           chart->data_bottom + chart->font_size * SUB_CATAGORIES * 1.5);
-
-  pl_alabel_r(chart->lp,0,'b',subcat_name);
-
-  for (sc = 0 ; sc < SUB_CATAGORIES ; ++sc )
-    {
-      pl_fmove_r(chart->lp,
-                chart->legend_left,
-                chart->data_bottom + chart->font_size * sc  * 1.5);
-
-      pl_savestate_r(chart->lp);
-      pl_fillcolorname_r(chart->lp,data_colour[sc]);
-      pl_fboxrel_r (chart->lp,
-                   0,0,
-                   chart->font_size, chart->font_size);
-      pl_restorestate_r(chart->lp);
-
-      pl_fmove_r(chart->lp,
-                chart->legend_left + chart->font_size * 1.5,
-                chart->data_bottom + chart->font_size * sc  * 1.5);
-
-      pl_alabel_r(chart->lp,'l','b',sub_catagory[sc].label);
-    }
-
-
-  pl_restorestate_r(chart->lp);
-}
diff --git a/src/output/charts/barchart.h b/src/output/charts/barchart.h
deleted file mode 100644 (file)
index 0983272..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 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 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.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef BARCHART_H
-#define BARCHART_H
-
-#include <output/chart.h>
-
-enum  bar_opts {
-  BAR_GROUPED =  0,
-  BAR_STACKED,
-  BAR_RANGE
-};
-
-void draw_barchart(struct chart *ch, const char *title,
-             const char *xlabel, const char *ylabel, enum bar_opts opt);
-
-#endif
index 33c445b09a61b0ef36714272bd221bfd2cd9f741..5fd9d295f749189aa20358712ba782bac0034b5a 100644 (file)
 
 #include <config.h>
 
+#include <output/charts/box-whisker.h>
+
 #include <math.h>
 #include <assert.h>
-#include <libpspp/misc.h>
-
-#include <output/charts/box-whisker.h>
-#include <output/charts/plot-chart.h>
+#include <cairo/cairo.h>
 
-#include <output/chart.h>
+#include <libpspp/cast.h>
+#include <libpspp/misc.h>
 #include <math/chart-geometry.h>
 #include <math/box-whisker.h>
+#include <output/chart.h>
+#include <output/chart-provider.h>
+#include <output/charts/plot-chart.h>
 
 /* Draw a box-and-whiskers plot
 */
 
+struct box
+  {
+    struct box_whisker *bw;
+    char *label;
+  };
+
+struct boxplot
+  {
+    struct chart chart;
+    double y_min;
+    double y_max;
+    char *title;
+    struct box *boxes;
+    size_t n_boxes, boxes_allocated;
+  };
+
+static const struct chart_class boxplot_chart_class;
+
+struct boxplot *
+boxplot_create (double y_min, double y_max, const char *title)
+{
+  struct boxplot *boxplot = xmalloc (sizeof *boxplot);
+  chart_init (&boxplot->chart, &boxplot_chart_class);
+  boxplot->y_min = y_min;
+  boxplot->y_max = y_max;
+  boxplot->title = xstrdup (title);
+  boxplot->boxes = NULL;
+  boxplot->n_boxes = boxplot->boxes_allocated = 0;
+  return boxplot;
+}
+
+void
+boxplot_add_box (struct boxplot *boxplot,
+                 struct box_whisker *bw, const char *label)
+{
+  struct box *box;
+  if (boxplot->n_boxes >= boxplot->boxes_allocated)
+    boxplot->boxes = x2nrealloc (boxplot->boxes, &boxplot->boxes_allocated,
+                                 sizeof *boxplot->boxes);
+  box = &boxplot->boxes[boxplot->n_boxes++];
+  box->bw = bw;
+  box->label = xstrdup (label);
+}
+
+struct chart *
+boxplot_get_chart (struct boxplot *boxplot)
+{
+  return &boxplot->chart;
+}
+
 /* Draw an OUTLIER on the plot CH
  * at CENTRELINE
  */
 static void
-draw_case (struct chart *ch, double centreline,
+draw_case (cairo_t *cr, const struct chart_geometry *geom, double centreline,
           const struct outlier *outlier)
 {
+  double y = geom->data_bottom + (outlier->value - geom->y_min) * geom->ordinate_scale;
+  chart_draw_marker (cr, centreline, y,
+                     outlier->extreme ? MARKER_ASTERISK : MARKER_CIRCLE,
+                     20);
 
-#define MARKER_CIRCLE 4
-#define MARKER_STAR 3
-
-  pl_fmarker_r(ch->lp,
-              centreline,
-              ch->data_bottom + (outlier->value - ch->y_min) * ch->ordinate_scale,
-              outlier->extreme ? MARKER_STAR : MARKER_CIRCLE,
-              20);
-
-  pl_moverel_r(ch->lp, 10,0);
-
-  pl_alabel_r(ch->lp, 'l', 'c', ds_cstr (&outlier->label));
+  cairo_move_to (cr, centreline + 10, y);
+  chart_label (cr, 'l', 'c', geom->font_size, ds_cstr (&outlier->label));
 }
 
-
-void
-boxplot_draw_boxplot (struct chart *ch,
-                     double box_centre,
-                     double box_width,
-                     const struct box_whisker *bw,
-                     const char *name)
+static void
+boxplot_draw_box (cairo_t *cr, const struct chart_geometry *geom,
+                  double box_centre,
+                  double box_width,
+                  const struct box_whisker *bw,
+                  const char *name)
 {
   double whisker[2];
   double hinge[3];
@@ -79,106 +125,139 @@ boxplot_draw_boxplot (struct chart *ch,
   box_whisker_whiskers (bw, whisker);
   box_whisker_hinges (bw, hinge);
 
-  box_bottom = ch->data_bottom + (hinge[0] - ch->y_min ) * ch->ordinate_scale;
-
-  box_top = ch->data_bottom + (hinge[2] - ch->y_min ) * ch->ordinate_scale;
+  box_bottom = geom->data_bottom + (hinge[0] - geom->y_min ) * geom->ordinate_scale;
 
-  bottom_whisker = ch->data_bottom + (whisker[0] - ch->y_min) *
-    ch->ordinate_scale;
+  box_top = geom->data_bottom + (hinge[2] - geom->y_min ) * geom->ordinate_scale;
 
-  top_whisker = ch->data_bottom + (whisker[1] - ch->y_min) * ch->ordinate_scale;
+  bottom_whisker = geom->data_bottom + (whisker[0] - geom->y_min) *
+    geom->ordinate_scale;
 
-  pl_savestate_r(ch->lp);
+  top_whisker = geom->data_bottom + (whisker[1] - geom->y_min) * geom->ordinate_scale;
 
   /* Draw the box */
-  pl_savestate_r (ch->lp);
-  pl_fillcolorname_r (ch->lp, ch->fill_colour);
-  pl_filltype_r (ch->lp,1);
-  pl_fbox_r (ch->lp,
-           box_left,
-           box_bottom,
-           box_right,
-           box_top);
-
-  pl_restorestate_r (ch->lp);
+  cairo_rectangle (cr,
+                   box_left,
+                   box_bottom,
+                   box_right - box_left,
+                   box_top - box_bottom);
+  cairo_save (cr);
+  cairo_set_source_rgb (cr,
+                        geom->fill_colour.red / 255.0,
+                        geom->fill_colour.green / 255.0,
+                        geom->fill_colour.blue / 255.0);
+  cairo_fill (cr);
+  cairo_restore (cr);
+  cairo_stroke (cr);
 
   /* Draw the median */
-  pl_savestate_r (ch->lp);
-  pl_linewidth_r (ch->lp, 5);
-  pl_fline_r (ch->lp,
-            box_left,
-            ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale,
-            box_right,
-            ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale);
-  pl_restorestate_r (ch->lp);
+  cairo_save (cr);
+  cairo_set_line_width (cr, cairo_get_line_width (cr) * 5);
+  cairo_move_to (cr,
+                 box_left,
+                 geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale);
+  cairo_line_to (cr,
+                 box_right,
+                 geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale);
+  cairo_stroke (cr);
+  cairo_restore (cr);
 
   /* Draw the bottom whisker */
-  pl_fline_r (ch->lp,
-            box_left,
-            bottom_whisker,
-            box_right,
-            bottom_whisker);
+  cairo_move_to (cr, box_left, bottom_whisker);
+  cairo_line_to (cr, box_right, bottom_whisker);
+  cairo_stroke (cr);
 
   /* Draw top whisker */
-  pl_fline_r (ch->lp,
-            box_left,
-            top_whisker,
-            box_right,
-            top_whisker);
-
+  cairo_move_to (cr, box_left, top_whisker);
+  cairo_line_to (cr, box_right, top_whisker);
+  cairo_stroke (cr);
 
   /* Draw centre line.
      (bottom half) */
-  pl_fline_r (ch->lp,
-            box_centre, bottom_whisker,
-            box_centre, box_bottom);
+  cairo_move_to (cr, box_centre, bottom_whisker);
+  cairo_line_to (cr, box_centre, box_bottom);
+  cairo_stroke (cr);
 
   /* (top half) */
-  pl_fline_r (ch->lp,
-            box_centre, top_whisker,
-            box_centre, box_top);
+  cairo_move_to (cr, box_centre, top_whisker);
+  cairo_line_to (cr, box_centre, box_top);
+  cairo_stroke (cr);
 
   outliers = box_whisker_outliers (bw);
   for (ll = ll_head (outliers);
        ll != ll_null (outliers); ll = ll_next (ll))
     {
       const struct outlier *outlier = ll_data (ll, struct outlier, ll);
-      draw_case (ch, box_centre, outlier);
+      draw_case (cr, geom, box_centre, outlier);
     }
 
   /* Draw  tick  mark on x axis */
-  draw_tick(ch, TICK_ABSCISSA, box_centre - ch->data_left, "%s", name);
-
-  pl_restorestate_r(ch->lp);
+  draw_tick(cr, geom, TICK_ABSCISSA, box_centre - geom->data_left, "%s", name);
 }
 
-void
-boxplot_draw_yscale (struct chart *ch, double y_max, double y_min)
+static void
+boxplot_draw_yscale (cairo_t *cr, struct chart_geometry *geom,
+                     double y_max, double y_min)
 {
   double y_tick;
   double d;
 
-  if ( !ch )
-     return ;
+  geom->y_max = y_max;
+  geom->y_min = y_min;
+
+  y_tick = chart_rounded_tick (fabs (geom->y_max - geom->y_min) / 5.0);
 
-  ch->y_max  = y_max;
-  ch->y_min  = y_min;
+  geom->y_min = (ceil (geom->y_min / y_tick) - 1.0) * y_tick;
 
-  y_tick = chart_rounded_tick (fabs(ch->y_max - ch->y_min) / 5.0);
+  geom->y_max = (floor (geom->y_max / y_tick) + 1.0) * y_tick;
 
-  ch->y_min = (ceil( ch->y_min  / y_tick ) - 1.0  ) * y_tick;
+  geom->ordinate_scale = (fabs (geom->data_top - geom->data_bottom)
+                          / fabs (geom->y_max - geom->y_min));
 
-  ch->y_max = ( floor( ch->y_max  / y_tick ) + 1.0  ) * y_tick;
+  for (d = geom->y_min; d <= geom->y_max; d += y_tick)
+    draw_tick (cr, geom, TICK_ORDINATE,
+               (d - geom->y_min) * geom->ordinate_scale, "%g", d);
+}
 
-  ch->ordinate_scale = fabs(ch->data_top - ch->data_bottom)
-    / fabs(ch->y_max - ch->y_min) ;
+static void
+boxplot_chart_draw (const struct chart *chart, cairo_t *cr,
+                    struct chart_geometry *geom)
+{
+  const struct boxplot *boxplot = UP_CAST (chart, struct boxplot, chart);
+  double box_width;
+  size_t i;
 
-  /* Move to data bottom-left */
-  pl_move_r(ch->lp,
-           ch->data_left, ch->data_bottom);
+  boxplot_draw_yscale (cr, geom, boxplot->y_max, boxplot->y_min);
+  chart_write_title (cr, geom, "%s", boxplot->title);
 
-  for ( d = ch->y_min; d <= ch->y_max ; d += y_tick )
+  box_width = (geom->data_right - geom->data_left) / boxplot->n_boxes / 2.0;
+  for (i = 0; i < boxplot->n_boxes; i++)
     {
-      draw_tick (ch, TICK_ORDINATE, (d - ch->y_min ) * ch->ordinate_scale, "%g", d);
+      const struct box *box = &boxplot->boxes[i];
+      const double box_centre = (i * 2 + 1) * box_width + geom->data_left;
+      boxplot_draw_box (cr, geom, box_centre, box_width, box->bw, box->label);
     }
 }
+
+static void
+boxplot_chart_destroy (struct chart *chart)
+{
+  struct boxplot *boxplot = UP_CAST (chart, struct boxplot, chart);
+  size_t i;
+
+  free (boxplot->title);
+  for (i = 0; i < boxplot->n_boxes; i++)
+    {
+      struct box *box = &boxplot->boxes[i];
+      struct statistic *statistic = &box->bw->parent.parent;
+      statistic->destroy (statistic);
+      free (box->label);
+    }
+  free (boxplot->boxes);
+  free (boxplot);
+}
+
+static const struct chart_class boxplot_chart_class =
+  {
+    boxplot_chart_draw,
+    boxplot_chart_destroy
+  };
index 7b2c4b8fdc1c2407c86cfe308a1c5ed4b72a31ba..67c1e1281192d640ae6a96bc0b20e3ed7130c272 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 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
 #ifndef BOX_WHISKER_H
 #define BOX_WHISKER_H
 
-struct chart ;
 struct box_whisker;
 
-void boxplot_draw_boxplot (struct chart *ch,
-                          double box_centre,
-                          double box_width,
-                          const struct box_whisker *w,
-                          const char *name);
-
-
-void boxplot_draw_yscale (struct chart *ch , double y_max, double y_min);
+struct boxplot *boxplot_create (double y_min, double y_max, const char *title);
+void boxplot_add_box (struct boxplot *,
+                      struct box_whisker *, const char *label);
+struct chart *boxplot_get_chart (struct boxplot *);
 
 #endif
index cb2f346b1b4134a0092cb322e3fe2a7a16177c55..eabcf516e8e1cdc7b84a148646c3aef38de956bc 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 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
 
 #include <config.h>
 
+#include <output/charts/cartesian.h>
+
+#include <cairo/cairo.h>
 #include <math.h>
 #include <assert.h>
 
 #include <output/chart.h>
-
+#include <output/chart-provider.h>
 #include <output/charts/plot-chart.h>
-#include <output/charts/cartesian.h>
 #include <libpspp/compiler.h>
 
+#include "xalloc.h"
 
 /* Start a new vector called NAME */
 void
-chart_vector_start (struct chart *ch, const char *name)
+chart_vector_start (cairo_t *cr, struct chart_geometry *geom, const char *name)
 {
-  if ( ! ch )
-    return ;
+  const struct chart_colour *colour;
 
-  pl_savestate_r (ch->lp);
+  cairo_save (cr);
 
-  pl_colorname_r (ch->lp, data_colour [ch->n_datasets % N_CHART_COLOURS]);
+  colour = &data_colour[geom->n_datasets % N_CHART_COLOURS];
+  cairo_set_source_rgb (cr,
+                        colour->red / 255.0,
+                        colour->green / 255.0,
+                        colour->blue / 255.0);
 
-  ch->n_datasets++;
-  ch->dataset = xrealloc (ch->dataset, ch->n_datasets * sizeof (*ch->dataset));
+  geom->n_datasets++;
+  geom->dataset = xrealloc (geom->dataset,
+                            geom->n_datasets * sizeof (*geom->dataset));
 
-  ch->dataset[ch->n_datasets - 1] = strdup (name);
+  geom->dataset[geom->n_datasets - 1] = strdup (name);
 }
 
 /* Plot a data point */
 void
-chart_datum (struct chart *ch, int dataset UNUSED, double x, double y)
+chart_datum (cairo_t *cr, const struct chart_geometry *geom,
+             int dataset UNUSED, double x, double y)
 {
-  if ( ! ch )
-    return ;
-
-  {
-    const double x_pos =
-      (x - ch->x_min) * ch->abscissa_scale + ch->data_left ;
+  double x_pos = (x - geom->x_min) * geom->abscissa_scale + geom->data_left;
+  double y_pos = (y - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
 
-    const double y_pos =
-      (y - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
-
-    pl_savestate_r(ch->lp);
-
-    pl_fmarker_r(ch->lp, x_pos, y_pos, 6, 15);
-
-    pl_restorestate_r(ch->lp);
-  }
+  chart_draw_marker (cr, x_pos, y_pos, MARKER_SQUARE, 15);
 }
 
 void
-chart_vector_end (struct chart *ch)
+chart_vector_end (cairo_t *cr, struct chart_geometry *geom)
 {
-  pl_endpath_r (ch->lp);
-  pl_colorname_r (ch->lp, "black");
-  ch->in_path = false;
-  pl_restorestate_r (ch->lp);
+  cairo_stroke (cr);
+  cairo_restore (cr);
+  geom->in_path = false;
 }
 
 /* Plot a data point */
 void
-chart_vector (struct chart *ch, double x, double y)
+chart_vector (cairo_t *cr, struct chart_geometry *geom, double x, double y)
 {
-  if ( ! ch )
-    return ;
-
-  {
-    const double x_pos =
-      (x - ch->x_min) * ch->abscissa_scale + ch->data_left ;
-
-    const double y_pos =
-      (y - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
-
-    if ( ch->in_path)
-      pl_fcont_r (ch->lp, x_pos, y_pos);
-    else
-      {
-       pl_fmove_r (ch->lp, x_pos, y_pos);
-       ch->in_path = true;
-      }
-  }
+  const double x_pos =
+    (x - geom->x_min) * geom->abscissa_scale + geom->data_left ;
+
+  const double y_pos =
+    (y - geom->y_min) * geom->ordinate_scale + geom->data_bottom ;
+
+  if (geom->in_path)
+    cairo_line_to (cr, x_pos, y_pos);
+  else
+    {
+      cairo_move_to (cr, x_pos, y_pos);
+      geom->in_path = true;
+    }
 }
 
 
@@ -107,20 +97,17 @@ chart_vector (struct chart *ch, double x, double y)
    y axis otherwise the x axis
 */
 void
-chart_line (struct chart *ch, double slope, double intercept,
+chart_line(cairo_t *cr, const struct chart_geometry *geom,
+           double slope, double intercept,
           double limit1, double limit2, enum CHART_DIM lim_dim)
 {
   double x1, y1;
-  double x2, y2 ;
-
-  if ( ! ch )
-    return ;
-
+  double x2, y2;
 
   if ( lim_dim == CHART_DIM_Y )
     {
-      x1 = ( limit1 - intercept ) / slope ;
-      x2 = ( limit2 - intercept ) / slope ;
+      x1 = ( limit1 - intercept ) / slope;
+      x2 = ( limit2 - intercept ) / slope;
       y1 = limit1;
       y2 = limit2;
     }
@@ -132,14 +119,12 @@ chart_line (struct chart *ch, double slope, double intercept,
       y2 = slope * x2 + intercept;
     }
 
-  y1 = (y1 - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
-  y2 = (y2 - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
-  x1 = (x1 - ch->x_min) * ch->abscissa_scale + ch->data_left ;
-  x2 = (x2 - ch->x_min) * ch->abscissa_scale + ch->data_left ;
-
-  pl_savestate_r(ch->lp);
-
-  pl_fline_r(ch->lp, x1, y1, x2, y2);
+  y1 = (y1 - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
+  y2 = (y2 - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
+  x1 = (x1 - geom->x_min) * geom->abscissa_scale + geom->data_left;
+  x2 = (x2 - geom->x_min) * geom->abscissa_scale + geom->data_left;
 
-  pl_restorestate_r(ch->lp);
+  cairo_move_to (cr, x1, y1);
+  cairo_line_to (cr, x2, y2);
+  cairo_stroke (cr);
 }
index 0874b9cc61d4d8f507188a00f07c44ca614176cf..3c21db6efc444c87cca867bbe184867dd1e707fe 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 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
@@ -19,6 +19,9 @@
 #ifndef CARTESIAN_H
 #define CARTESIAN_H
 
+#include <cairo/cairo.h>
+#include <libpspp/compiler.h>
+#include <output/chart.h>
 
 enum CHART_DIM
   {
@@ -26,20 +29,24 @@ enum CHART_DIM
     CHART_DIM_Y
   };
 
+struct chart_geometry;
 
-void chart_vector_start (struct chart *ch, const char *name);
-void chart_vector (struct chart *ch, double x, double y);
-void chart_vector_end (struct chart *ch);
+void  chart_vector_start (cairo_t *, struct chart_geometry *,
+                          const char *name);
+void chart_vector_end (cairo_t *, struct chart_geometry *);
+void chart_vector (cairo_t *, struct chart_geometry *, double x, double y);
 
 /* Plot a data point */
-void chart_datum (struct chart *ch, int dataset UNUSED, double x, double y);
+void chart_datum(cairo_t *, const struct chart_geometry *,
+                 int dataset UNUSED, double x, double y);
 
 /* Draw a line with slope SLOPE and intercept INTERCEPT.
    between the points limit1 and limit2.
    If lim_dim is CHART_DIM_Y then the limit{1,2} are on the
    y axis otherwise the x axis
 */
-void chart_line (struct chart *ch, double slope, double intercept,
+void chart_line(cairo_t *, const struct chart_geometry *,
+                double slope, double intercept,
                double limit1, double limit2, enum CHART_DIM lim_dim);
 
 
diff --git a/src/output/charts/dummy-chart.c b/src/output/charts/dummy-chart.c
deleted file mode 100644 (file)
index e22f958..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2005 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 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.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-
-/* Stubs for plotting routines.
-   This module is linked only when charts are not supported */
-
-#include "config.h"
-#include <output/chart.h>
-#include <output/charts/box-whisker.h>
-#include <output/charts/piechart.h>
-#include <output/charts/plot-chart.h>
-#include <output/charts/plot-hist.h>
-#include <output/charts/cartesian.h>
-#include <gsl/gsl_histogram.h>
-#include <libpspp/compiler.h>
-
-#ifndef NO_CHARTS
-#error This file should be used only when compiling without charts.
-#endif
-
-void
-chart_write_title (struct chart *chart UNUSED, const char *title UNUSED, ...)
-{
-}
-
-
-void
-chart_write_xscale (struct chart *ch UNUSED,
-                   double min UNUSED, double max UNUSED, int ticks UNUSED)
-{
-}
-
-
-void
-chart_write_yscale (struct chart *ch UNUSED UNUSED,
-                   double smin UNUSED, double smax UNUSED, int ticks UNUSED)
-{
-}
-
-
-void
-chart_write_xlabel (struct chart *ch UNUSED, const char *label UNUSED)
-{
-}
-
-void
-chart_write_ylabel (struct chart *ch UNUSED, const char *label UNUSED)
-{
-}
-
-
-void
-chart_line (struct chart *ch UNUSED,
-           double slope UNUSED, double intercept UNUSED,
-           double limit1 UNUSED, double limit2 UNUSED,
-           enum CHART_DIM lim_dim UNUSED)
-{
-}
-
-
-void
-chart_datum (struct chart *ch UNUSED, int dataset UNUSED UNUSED,
-            double x UNUSED, double y UNUSED)
-{
-}
-
-void
-histogram_plot (const struct histogram *hist UNUSED,
-               const char *label UNUSED,
-               const struct moments1 *m UNUSED)
-{
-}
-
-void
-histogram_plot_n (const struct histogram *hist UNUSED,
-                 const char *label UNUSED,
-                 double n UNUSED, double mean UNUSED, double stddev UNUSED,
-                 bool show_normal UNUSED)
-{
-}
-
-
-void
-boxplot_draw_yscale (struct chart *ch UNUSED,
-                    double y_max UNUSED, double y_min UNUSED)
-{
-}
-
-void
-boxplot_draw_boxplot (struct chart *ch UNUSED,
-                     double box_centre UNUSED,
-                     double box_width UNUSED,
-                     const struct box_whisker *w UNUSED,
-                     const char *name UNUSED)
-{
-}
-
-
-
-
-void
-piechart_plot (const char *title UNUSED,
-              const struct slice *slices UNUSED, int n_slices UNUSED)
-{
-}
diff --git a/src/output/charts/np-plot.c b/src/output/charts/np-plot.c
new file mode 100644 (file)
index 0000000..c077b87
--- /dev/null
@@ -0,0 +1,196 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2004, 2008, 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/charts/np-plot.h>
+
+#include <gsl/gsl_cdf.h>
+
+#include <data/casereader.h>
+#include <data/casewriter.h>
+#include <libpspp/cast.h>
+#include <libpspp/message.h>
+#include <math/np.h>
+#include <output/chart-provider.h>
+#include <output/charts/cartesian.h>
+#include <output/charts/plot-chart.h>
+
+#include "gl/minmax.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* An NP or DNP plot. */
+struct np_plot_chart
+  {
+    struct chart chart;
+    char *label;
+    struct casereader *data;
+
+    /* Copied directly from struct np. */
+    double y_min, y_max;
+    double dns_min, dns_max;
+
+    /* Calculated. */
+    double slope, intercept;
+    double y_first, y_last;
+    double x_lower, x_upper;
+    double slack;
+  };
+
+static const struct chart_class np_plot_chart_class;
+static const struct chart_class dnp_plot_chart_class;
+
+static struct chart *
+make_np_plot (const struct chart_class *class,
+              const struct np *np, const struct casereader *reader,
+              const char *label)
+{
+  struct np_plot_chart *npp;
+
+  if (np->n < 1.0)
+    return NULL;
+
+  npp = xmalloc (sizeof *npp);
+  chart_init (&npp->chart, class);
+  npp->label = xstrdup (label);
+  npp->data = casereader_clone (reader);
+  npp->y_min = np->y_min;
+  npp->y_max = np->y_max;
+  npp->dns_min = np->dns_min;
+  npp->dns_max = np->dns_max;
+
+  /* Slope and intercept of the ideal normal probability line. */
+  npp->slope = 1.0 / np->stddev;
+  npp->intercept = -np->mean / np->stddev;
+
+  npp->y_first = gsl_cdf_ugaussian_Pinv (1 / (np->n + 1));
+  npp->y_last = gsl_cdf_ugaussian_Pinv (np->n / (np->n + 1));
+
+  /* Need to make sure that both the scatter plot and the ideal fit into the
+     plot. */
+  npp->x_lower = MIN (np->y_min, (npp->y_first - npp->intercept) / npp->slope);
+  npp->x_upper = MAX (np->y_max, (npp->y_last  - npp->intercept) / npp->slope);
+  npp->slack = (npp->x_upper - npp->x_lower) * 0.05;
+
+  return &npp->chart;
+}
+
+/* Creates and returns a normal probability plot corresponding to
+   the calculations in NP and the data in READER, and label the
+   plot with LABEL.  The data in READER must have Y-values in
+   value index NP_IDX_Y and NS-values in value index NP_IDX_NS.
+
+   Returns a null pointer if the data set is empty.
+
+   The caller retains ownership of NP and READER. */
+struct chart *
+np_plot_create (const struct np *np, const struct casereader *reader,
+                const char *label)
+{
+  return make_np_plot (&np_plot_chart_class, np, reader, label);
+}
+
+/* Creates and returns a detrended normal probability plot
+   corresponding to the calculations in NP and the data in
+   READER, and label the plot with LABEL.  The data in READER
+   must have Y-values in value index NP_IDX_Y and DNS-values in
+   value index NP_IDX_DNS.
+
+   Returns a null pointer if the data set is empty.
+
+   The caller retains ownership of NP and READER. */
+struct chart *
+dnp_plot_create (const struct np *np, const struct casereader *reader,
+                 const char *label)
+{
+  return make_np_plot (&dnp_plot_chart_class, np, reader, label);
+}
+
+static void
+np_plot_chart_draw (const struct chart *chart, cairo_t *cr,
+                    struct chart_geometry *geom)
+{
+  const struct np_plot_chart *npp = UP_CAST (chart, struct np_plot_chart,
+                                             chart);
+  struct casereader *data;
+  struct ccase *c;
+
+  chart_write_title (cr, geom, _("Normal Q-Q Plot of %s"), npp->label);
+  chart_write_xlabel (cr, geom, _("Observed Value"));
+  chart_write_ylabel (cr, geom, _("Expected Normal"));
+  chart_write_xscale (cr, geom,
+                      npp->x_lower - npp->slack,
+                      npp->x_upper + npp->slack, 5);
+  chart_write_yscale (cr, geom, npp->y_first, npp->y_last, 5);
+
+  data = casereader_clone (npp->data);
+  for (; (c = casereader_read (data)) != NULL; case_unref (c))
+    chart_datum (cr, geom, 0,
+                 case_data_idx (c, NP_IDX_Y)->f,
+                 case_data_idx (c, NP_IDX_NS)->f);
+  casereader_destroy (data);
+
+  chart_line (cr, geom, npp->slope, npp->intercept,
+              npp->y_first, npp->y_last, CHART_DIM_Y);
+}
+
+static void
+dnp_plot_chart_draw (const struct chart *chart, cairo_t *cr,
+                     struct chart_geometry *geom)
+{
+  const struct np_plot_chart *dnpp = UP_CAST (chart, struct np_plot_chart,
+                                              chart);
+  struct casereader *data;
+  struct ccase *c;
+
+  chart_write_title (cr, geom, _("Detrended Normal Q-Q Plot of %s"),
+                     dnpp->label);
+  chart_write_xlabel (cr, geom, _("Observed Value"));
+  chart_write_ylabel (cr, geom, _("Dev from Normal"));
+  chart_write_xscale (cr, geom, dnpp->y_min, dnpp->y_max, 5);
+  chart_write_yscale (cr, geom, dnpp->dns_min, dnpp->dns_max, 5);
+
+  data = casereader_clone (dnpp->data);
+  for (; (c = casereader_read (data)) != NULL; case_unref (c))
+    chart_datum (cr, geom, 0, case_data_idx (c, NP_IDX_Y)->f,
+                 case_data_idx (c, NP_IDX_DNS)->f);
+  casereader_destroy (data);
+
+  chart_line (cr, geom, 0, 0, dnpp->y_min, dnpp->y_max, CHART_DIM_X);
+}
+
+static void
+np_plot_chart_destroy (struct chart *chart)
+{
+  struct np_plot_chart *npp = UP_CAST (chart, struct np_plot_chart, chart);
+  casereader_destroy (npp->data);
+  free (npp->label);
+  free (npp);
+}
+
+static const struct chart_class np_plot_chart_class =
+  {
+    np_plot_chart_draw,
+    np_plot_chart_destroy
+  };
+
+static const struct chart_class dnp_plot_chart_class =
+  {
+    dnp_plot_chart_draw,
+    np_plot_chart_destroy
+  };
diff --git a/src/output/charts/np-plot.h b/src/output/charts/np-plot.h
new file mode 100644 (file)
index 0000000..c974235
--- /dev/null
@@ -0,0 +1,28 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2004, 2008, 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CHARTS_NP_PLOT_H
+#define OUTPUT_CHARTS_NP_PLOT_H 1
+
+struct casereader;
+struct np;
+
+struct chart *np_plot_create (const struct np *, const struct casereader *,
+                              const char *label);
+struct chart *dnp_plot_create (const struct np *, const struct casereader *,
+                               const char *label);
+
+#endif /* output/charts/np-plot.h */
index d7db80e921a6f573991ed0be14e1eaf583e8527d..935c6eb0fed9784024c54e571e53936e07c09c7b 100644 (file)
 
 #include <config.h>
 
-#include <float.h>
+#include <output/charts/piechart.h>
+
 #include <assert.h>
+#include <float.h>
+#include <gsl/gsl_math.h>
 #include <math.h>
 #include <stdio.h>
 
-
-#include <output/charts/piechart.h>
-#include <output/charts/plot-chart.h>
-
-#include <output/chart.h>
-#include <libpspp/str.h>
 #include <data/value-labels.h>
+#include <libpspp/cast.h>
+#include <libpspp/str.h>
+#include <output/charts/plot-chart.h>
+#include <output/chart-provider.h>
 
 #include "minmax.h"
 
+struct piechart
+  {
+    struct chart chart;
+    char *title;
+    struct slice *slices;
+    int n_slices;
+  };
 
-/* Pie charts of course need to know Pi :) */
-#ifndef M_PI
-#define M_PI ( 22.0 / 7.0 )
-#endif
-
-
+static const struct chart_class piechart_class;
 
 /* Draw a single slice of the pie */
 static void
-draw_segment(struct chart *ch,
+draw_segment(cairo_t *,
             double centre_x, double centre_y,
             double radius,
             double start_angle, double segment_angle,
-            const char *colour) ;
+            const struct chart_colour *) ;
 
 
 
-/* Draw a piechart */
-void
-piechart_plot(const char *title, const struct slice *slices, int n_slices)
+/* Creates and returns a chart that will render a piechart with
+   the given TITLE and the N_SLICES described in SLICES. */
+struct chart *
+piechart_create (const char *title, const struct slice *slices, int n_slices)
 {
+  struct piechart *pie;
   int i;
-  double total_magnetude=0;
-
-  struct chart *ch;
 
-  double left_label;
-  double right_label;
+  pie = xmalloc (sizeof *pie);
+  chart_init (&pie->chart, &piechart_class);
+  pie->title = xstrdup (title);
+  pie->slices = xnmalloc (n_slices, sizeof *pie->slices);
+  for (i = 0; i < n_slices; i++)
+    {
+      const struct slice *src = &slices[i];
+      struct slice *dst = &pie->slices[i];
 
-  double centre_x;
-  double centre_y;
+      ds_init_string (&dst->label, &src->label);
+      dst->magnitude = src->magnitude;
+    }
+  pie->n_slices = n_slices;
+  return &pie->chart;
+}
 
+static void
+piechart_draw (const struct chart *chart, cairo_t *cr,
+               struct chart_geometry *geom)
+{
+  const struct piechart *pie = UP_CAST (chart, struct piechart, chart);
+  double total_magnitude;
+  double left_label, right_label;
+  double centre_x, centre_y;
   double radius;
+  double angle;
+  int i;
 
-  ch = chart_create ();
-  if (ch == NULL)
-    return;
+  centre_x = (geom->data_right + geom->data_left) / 2.0 ;
+  centre_y = (geom->data_top + geom->data_bottom) / 2.0 ;
 
-  left_label = ch->data_left + (ch->data_right - ch->data_left)/10.0;
-  right_label = ch->data_right - (ch->data_right - ch->data_left)/10.0;
-  centre_x = (ch->data_right + ch->data_left ) / 2.0;
-  centre_y = (ch->data_top + ch->data_bottom ) / 2.0;
-  radius = MIN (5.0 / 12.0 * (ch->data_top - ch->data_bottom),
-                1.0 / 4.0 * (ch->data_right - ch->data_left));
+  left_label = geom->data_left + (geom->data_right - geom->data_left)/10.0;
+  right_label = geom->data_right - (geom->data_right - geom->data_left)/10.0;
 
-  chart_write_title(ch, "%s", title);
+  radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom),
+                1.0 / 4.0 * (geom->data_right - geom->data_left));
 
-  for (i = 0 ; i < n_slices ; ++i )
-    total_magnetude += slices[i].magnetude;
+  radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom),
+                1.0 / 4.0 * (geom->data_right - geom->data_left));
 
-  for (i = 0 ; i < n_slices ; ++i )
-    {
-      static double angle=0.0;
+  chart_write_title (cr, geom, "%s", pie->title);
 
+  total_magnitude = 0.0;
+  for (i = 0; i < pie->n_slices; i++)
+    total_magnitude += pie->slices[i].magnitude;
+
+  angle = 0.0;
+  for (i = 0; i < pie->n_slices ; ++i )
+    {
       const double segment_angle =
-       slices[i].magnetude / total_magnetude * 2 * M_PI ;
+       pie->slices[i].magnitude / total_magnitude * 2 * M_PI ;
 
       const double label_x = centre_x -
        radius * sin(angle + segment_angle/2.0);
@@ -97,120 +120,78 @@ piechart_plot(const char *title, const struct slice *slices, int n_slices)
        radius * cos(angle + segment_angle/2.0);
 
       /* Fill the segment */
-      draw_segment(ch,
-                  centre_x, centre_y, radius,
-                  angle, segment_angle,
-                  data_colour[i % N_CHART_COLOURS]);
+      draw_segment (cr,
+                    centre_x, centre_y, radius,
+                    angle, segment_angle,
+                    &data_colour[i % N_CHART_COLOURS]);
 
       /* Now add the labels */
       if ( label_x < centre_x )
        {
-         pl_line_r(ch->lp, label_x, label_y,
-                   left_label, label_y );
-         pl_moverel_r(ch->lp,0,5);
-         pl_alabel_r (ch->lp, 0, 0, ds_cstr (&slices[i].label));
+          cairo_move_to (cr, label_x, label_y);
+          cairo_line_to (cr, left_label, label_y);
+          cairo_stroke (cr);
+         cairo_move_to (cr, left_label, label_y + 5);
+         chart_label (cr, 'l', 'x', geom->font_size,
+                       ds_cstr (&pie->slices[i].label));
        }
       else
        {
-         pl_line_r(ch->lp,
-                   label_x, label_y,
-                   right_label, label_y
-                   );
-         pl_moverel_r(ch->lp,0,5);
-         pl_alabel_r (ch->lp, 'r', 0, ds_cstr (&slices[i].label));
+         cairo_move_to (cr, label_x, label_y);
+          cairo_line_to (cr, right_label, label_y);
+          cairo_stroke (cr);
+         cairo_move_to (cr, right_label, label_y + 5);
+         chart_label (cr, 'r', 'x', geom->font_size,
+                       ds_cstr (&pie->slices[i].label));
        }
 
       angle += segment_angle;
-
     }
 
   /* Draw an outline to the pie */
-  pl_filltype_r(ch->lp,0);
-  pl_fcircle_r (ch->lp, centre_x, centre_y, radius);
-
-  chart_submit(ch);
+  cairo_arc (cr, centre_x, centre_y, radius, 0, 2 * M_PI);
+  cairo_stroke (cr);
 }
 
+/* Draw a single slice of the pie */
 static void
-fill_segment(struct chart *ch,
-            double x0, double y0,
-            double radius,
-            double start_angle, double segment_angle) ;
-
-
-/* Fill a segment with the current fill colour */
-static void
-fill_segment(struct chart *ch,
+draw_segment(cairo_t *cr,
             double x0, double y0,
             double radius,
-            double start_angle, double segment_angle)
+            double start_angle, double segment_angle,
+            const struct chart_colour *colour)
 {
-
-  const double start_x  = x0 - radius * sin(start_angle);
-  const double start_y  = y0 + radius * cos(start_angle);
-
-  const double stop_x   =
-    x0 - radius * sin(start_angle + segment_angle);
-
-  const double stop_y   =
-    y0 + radius * cos(start_angle + segment_angle);
-
-  assert(segment_angle <= 2 * M_PI);
-  assert(segment_angle >= 0);
-
-  if ( segment_angle > M_PI )
-    {
-      /* Then we must draw it in two halves */
-      fill_segment(ch, x0, y0, radius, start_angle, segment_angle / 2.0 );
-      fill_segment(ch, x0, y0, radius, start_angle + segment_angle / 2.0,
-                  segment_angle / 2.0 );
-    }
-  else
-    {
-      pl_move_r(ch->lp, x0, y0);
-
-      pl_cont_r(ch->lp, stop_x, stop_y);
-      pl_cont_r(ch->lp, start_x, start_y);
-
-      pl_arc_r(ch->lp,
-              x0, y0,
-              stop_x, stop_y,
-              start_x, start_y
-              );
-
-      pl_endpath_r(ch->lp);
-    }
+  cairo_move_to (cr, x0, y0);
+  cairo_arc (cr, x0, y0, radius, start_angle, start_angle + segment_angle);
+  cairo_line_to (cr, x0, y0);
+  cairo_save (cr);
+  cairo_set_source_rgb (cr,
+                        colour->red / 255.0,
+                        colour->green / 255.0,
+                        colour->blue / 255.0);
+  cairo_fill_preserve (cr);
+  cairo_restore (cr);
+  cairo_stroke (cr);
 }
 
-
-
-/* Draw a single slice of the pie */
 static void
-draw_segment(struct chart *ch,
-            double x0, double y0,
-            double radius,
-            double start_angle, double segment_angle,
-            const char *colour)
+piechart_destroy (struct chart *chart)
 {
-  const double start_x  = x0 - radius * sin(start_angle);
-  const double start_y  = y0 + radius * cos(start_angle);
-
-  pl_savestate_r(ch->lp);
-
-  pl_savestate_r(ch->lp);
-  pl_colorname_r(ch->lp, colour);
-
-  pl_pentype_r(ch->lp,1);
-  pl_filltype_r(ch->lp,1);
-
-  fill_segment(ch, x0, y0, radius, start_angle, segment_angle);
-  pl_restorestate_r(ch->lp);
-
-  /* Draw line dividing segments */
-  pl_pentype_r(ch->lp, 1);
-  pl_fline_r(ch->lp, x0, y0, start_x, start_y);
-
+  struct piechart *pie = UP_CAST (chart, struct piechart, chart);
+  int i;
 
-  pl_restorestate_r(ch->lp);
+  free (pie->title);
+  for (i = 0; i < pie->n_slices; i++)
+    {
+      struct slice *slice = &pie->slices[i];
+      ds_destroy (&slice->label);
+    }
+  free (pie->slices);
+  free (pie);
 }
 
+static const struct chart_class piechart_class =
+  {
+    piechart_draw,
+    piechart_destroy
+  };
index 96540401e7406f61e33259a7117e983d18e78fb9..39a0c2d5d28e7c05766b93e62a4f0d91b6f4e292 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 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
 
 struct slice {
   struct string label;
-  double magnetude;
+  double magnitude;
 };
 
-/* Draw a piechart */
-void piechart_plot(const char *title,
-                  const struct slice *slices, int n_slices);
+struct chart *piechart_create (const char *title,
+                               const struct slice *, int n_slices);
 
 #endif
 
index 5641db1213be2cd070d66cffb7daf479823076f6..cda6d1ec83ce5f431d6e75f93e3a607005308bc3 100644 (file)
 
 #include <config.h>
 
-#include <stdio.h>
-#include <plot.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdio.h>
-#include <float.h>
-#include <assert.h>
-#include <math.h>
-
 #include <output/charts/plot-chart.h>
 
-#include <math/chart-geometry.h>
-
-
+#include <assert.h>
+#include <float.h>
+#include <math.h>
+#include <pango/pango-font.h>
+#include <pango/pango-layout.h>
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdio.h>
+#include <string.h>
 
-#include <libpspp/str.h>
 #include <libpspp/assertion.h>
+#include <libpspp/str.h>
+#include <math/chart-geometry.h>
+#include <output/chart-provider.h>
 #include <output/manager.h>
 #include <output/output.h>
 
 #include "xalloc.h"
 
-const char *const data_colour[N_CHART_COLOURS] =
+#if ! PANGO_VERSION_CHECK (2, 22, 0)
+int pango_layout_get_baseline (PangoLayout    *layout);
+
+/* Shamelessly copied from the pango source */
+int
+pango_layout_get_baseline (PangoLayout    *layout)
+{
+  int baseline;
+
+  /* XXX this is so inefficient */
+  PangoLayoutIter *iter = pango_layout_get_iter (layout);
+  baseline = pango_layout_iter_get_baseline (iter);
+  pango_layout_iter_free (iter);
+
+  return baseline;
+}
+#endif
+
+
+
+const struct chart_colour data_colour[N_CHART_COLOURS] =
   {
-    "brown",
-    "red",
-    "orange",
-    "yellow",
-    "green",
-    "blue",
-    "violet",
-    "grey",
-    "pink"
+    { 165, 42, 42 },            /* brown */
+    { 255, 0, 0 },              /* red */
+    { 255, 165, 0 },            /* orange */
+    { 255, 255, 0 },            /* yellow */
+    { 0, 255, 0 },              /* green */
+    { 0, 0, 255 },              /* blue */
+    { 238, 130, 238 },          /* violet */
+    { 190, 190, 190 },          /* grey */
+    { 255, 192, 203 },          /* pink */
   };
 
+void
+chart_draw_marker (cairo_t *cr, double x, double y, enum marker_type marker,
+                   double size)
+{
+  cairo_save (cr);
+  cairo_translate (cr, x, y);
+  cairo_scale (cr, size / 2.0, size / 2.0);
+  cairo_set_line_width (cr, cairo_get_line_width (cr) / (size / 2.0));
+  switch (marker)
+    {
+    case MARKER_CIRCLE:
+      cairo_arc (cr, 0, 0, 1.0, 0, 2 * M_PI);
+      cairo_stroke (cr);
+      break;
+
+    case MARKER_ASTERISK:
+      cairo_move_to (cr, 0, -1.0); /* | */
+      cairo_line_to (cr, 0, 1.0);
+      cairo_move_to (cr, -M_SQRT1_2, -M_SQRT1_2); /* / */
+      cairo_line_to (cr, M_SQRT1_2, M_SQRT1_2);
+      cairo_move_to (cr, -M_SQRT1_2, M_SQRT1_2); /* \ */
+      cairo_line_to (cr, M_SQRT1_2, -M_SQRT1_2);
+      cairo_stroke (cr);
+      break;
+
+    case MARKER_SQUARE:
+      cairo_rectangle (cr, -1.0, -1.0, 2.0, 2.0);
+      cairo_stroke (cr);
+      break;
+    }
+  cairo_restore (cr);
+}
 
+void
+chart_label (cairo_t *cr, int horz_justify, int vert_justify, double font_size,
+             const char *string)
+{
+  PangoFontDescription *desc;
+  PangoLayout *layout;
+  double x, y;
+
+  desc = pango_font_description_from_string ("sans serif");
+  if (desc == NULL)
+    {
+      cairo_new_path (cr);
+      return;
+    }
+  pango_font_description_set_absolute_size (desc, font_size * PANGO_SCALE);
+
+  cairo_save (cr);
+  cairo_get_current_point (cr, &x, &y);
+  cairo_translate (cr, x, y);
+  cairo_move_to (cr, 0, 0);
+  cairo_scale (cr, 1.0, -1.0);
+
+  layout = pango_cairo_create_layout (cr);
+  pango_layout_set_font_description (layout, desc);
+  pango_layout_set_text (layout, string, -1);
+  if (horz_justify != 'l')
+    {
+      int width_pango;
+      double width;
+
+      pango_layout_get_size (layout, &width_pango, NULL);
+      width = (double) width_pango / PANGO_SCALE;
+      if (horz_justify == 'r')
+        cairo_rel_move_to (cr, -width, 0);
+      else
+        cairo_rel_move_to (cr, -width / 2.0, 0);
+    }
+  if (vert_justify == 'x')
+    {
+      int baseline_pango = pango_layout_get_baseline (layout);
+      double baseline = (double) baseline_pango / PANGO_SCALE;
+      cairo_rel_move_to (cr, 0, -baseline);
+    }
+  else if (vert_justify != 't')
+    {
+      int height_pango;
+      double height;
+
+      pango_layout_get_size (layout, NULL, &height_pango);
+      height = (double) height_pango / PANGO_SCALE;
+      if (vert_justify == 'b')
+        cairo_rel_move_to (cr, 0, -height);
+      else if (vert_justify == 'c')
+        cairo_rel_move_to (cr, 0, -height / 2.0);
+    }
+  pango_cairo_show_layout (cr, layout);
+  g_object_unref (layout);
+
+  cairo_restore (cr);
+
+  cairo_new_path (cr);
+
+  pango_font_description_free (desc);
+}
 
 /* Draw a tick mark at position
    If label is non zero, then print it at the tick mark
 */
 void
-draw_tick(struct chart *chart,
-         enum tick_orientation orientation,
-         double position,
-         const char *label, ...)
+draw_tick (cairo_t *cr, const struct chart_geometry *geom,
+           enum tick_orientation orientation,
+           double position,
+           const char *label, ...)
 {
   const int tickSize = 10;
+  double x, y;
 
-  assert(chart);
-
-  pl_savestate_r(chart->lp);
+  cairo_move_to (cr, geom->data_left, geom->data_bottom);
 
-  pl_move_r(chart->lp, chart->data_left, chart->data_bottom);
-
-  if ( orientation == TICK_ABSCISSA )
-    pl_flinerel_r(chart->lp, position, 0, position, -tickSize);
-  else if (orientation == TICK_ORDINATE )
-      pl_flinerel_r(chart->lp, 0, position, -tickSize, position);
+  if (orientation == TICK_ABSCISSA)
+    {
+      cairo_rel_move_to (cr, position, 0);
+      cairo_rel_line_to (cr, 0, -tickSize);
+    }
+  else if (orientation == TICK_ORDINATE)
+    {
+      cairo_rel_move_to (cr, 0, position);
+      cairo_rel_line_to (cr, -tickSize, 0);
+    }
   else
     NOT_REACHED ();
+  cairo_get_current_point (cr, &x, &y);
 
-  if ( label ) {
-    char buf[10];
-    va_list ap;
-    va_start(ap,label);
-    vsnprintf(buf,10,label,ap);
+  cairo_stroke (cr);
 
-    if ( orientation == TICK_ABSCISSA )
-      pl_alabel_r(chart->lp, 'c','t', buf);
-    else if (orientation == TICK_ORDINATE )
-      {
-       if ( fabs(position) < DBL_EPSILON )
-           pl_moverel_r(chart->lp, 0, 10);
-
-       pl_alabel_r(chart->lp, 'r','c', buf);
-      }
-
-    va_end(ap);
-  }
-
-  pl_restorestate_r(chart->lp);
+  if (label != NULL)
+    {
+      va_list ap;
+      char *s;
+
+      cairo_move_to (cr, x, y);
+
+      va_start (ap, label);
+      s = xvasprintf (label, ap);
+      if (orientation == TICK_ABSCISSA)
+        chart_label (cr, 'c', 't', geom->font_size, s);
+      else if (orientation == TICK_ORDINATE)
+        {
+          if (fabs (position) < DBL_EPSILON)
+           cairo_rel_move_to (cr, 0, 10);
+          chart_label (cr, 'r', 'c', geom->font_size, s);
+        }
+      free (s);
+      va_end (ap);
+    }
 }
 
 
 /* Write the title on a chart*/
 void
-chart_write_title(struct chart *chart, const char *title, ...)
+chart_write_title (cairo_t *cr, const struct chart_geometry *geom,
+                   const char *title, ...)
 {
   va_list ap;
-  char buf[100];
-
-  if ( ! chart )
-         return ;
+  char *s;
 
-  pl_savestate_r(chart->lp);
-  pl_ffontsize_r(chart->lp,chart->font_size * 1.5);
-  pl_move_r(chart->lp,chart->data_left, chart->title_bottom);
+  cairo_save (cr);
+  cairo_move_to (cr, geom->data_left, geom->title_bottom);
 
-  va_start(ap,title);
-  vsnprintf(buf,100,title,ap);
-  pl_alabel_r(chart->lp,0,0,buf);
-  va_end(ap);
+  va_start(ap, title);
+  s = xvasprintf (title, ap);
+  chart_label (cr, 'l', 'x', geom->font_size * 1.5, s);
+  free (s);
+  va_end (ap);
 
-  pl_restorestate_r(chart->lp);
+  cairo_restore (cr);
 }
 
 
 /* Set the scale for the abscissa */
 void
-chart_write_xscale(struct chart *ch, double min, double max, int ticks)
+chart_write_xscale (cairo_t *cr, struct chart_geometry *geom,
+                    double min, double max, int ticks)
 {
   double x;
 
   const double tick_interval =
-    chart_rounded_tick(max - min) / (double) ticks);
+    chart_rounded_tick ((max - min) / (double) ticks);
 
-  assert ( ch );
-
-
-  ch->x_max = ceil( max / tick_interval ) * tick_interval ;
-  ch->x_min = floor ( min / tick_interval ) * tick_interval ;
-
-
-  ch->abscissa_scale = fabs(ch->data_right - ch->data_left) /
-    fabs(ch->x_max - ch->x_min);
-
-  for(x = ch->x_min ; x <= ch->x_max; x += tick_interval )
-    {
-      draw_tick (ch, TICK_ABSCISSA,
-                (x - ch->x_min) * ch->abscissa_scale, "%g", x);
-    }
+  geom->x_max = ceil (max / tick_interval) * tick_interval;
+  geom->x_min = floor (min / tick_interval) * tick_interval;
+  geom->abscissa_scale = fabs(geom->data_right - geom->data_left) /
+    fabs(geom->x_max - geom->x_min);
 
+  for (x = geom->x_min; x <= geom->x_max; x += tick_interval)
+    draw_tick (cr, geom, TICK_ABSCISSA,
+               (x - geom->x_min) * geom->abscissa_scale, "%g", x);
 }
 
 
 /* Set the scale for the ordinate */
 void
-chart_write_yscale(struct chart *ch, double smin, double smax, int ticks)
+chart_write_yscale (cairo_t *cr, struct chart_geometry *geom,
+                    double smin, double smax, int ticks)
 {
   double y;
 
   const double tick_interval =
-    chart_rounded_tick( (smax - smin) / (double) ticks);
-
-  if ( !ch )
-         return;
+    chart_rounded_tick ((smax - smin) / (double) ticks);
 
-  ch->y_max = ceil  ( smax / tick_interval ) * tick_interval ;
-  ch->y_min = floor ( smin / tick_interval ) * tick_interval ;
+  geom->y_max = ceil (smax / tick_interval) * tick_interval;
+  geom->y_min = floor (smin / tick_interval) * tick_interval;
 
-  ch->ordinate_scale =
-    fabs(ch->data_top -  ch->data_bottom) / fabs(ch->y_max - ch->y_min) ;
+  geom->ordinate_scale =
+    (fabs (geom->data_top - geom->data_bottom)
+     / fabs (geom->y_max - geom->y_min));
 
-  for(y = ch->y_min ; y <= ch->y_max; y += tick_interval )
-    {
-    draw_tick (ch, TICK_ORDINATE,
-              (y - ch->y_min) * ch->ordinate_scale, "%g", y);
-    }
+  for (y = geom->y_min; y <= geom->y_max; y += tick_interval)
+    draw_tick (cr, geom, TICK_ORDINATE,
+              (y - geom->y_min) * geom->ordinate_scale, "%g", y);
 }
 
-
 /* Write the abscissa label */
 void
-chart_write_xlabel(struct chart *ch, const char *label)
+chart_write_xlabel (cairo_t *cr, const struct chart_geometry *geom,
+                    const char *label)
 {
-  if ( ! ch )
-    return ;
-
-  pl_savestate_r(ch->lp);
-
-  pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
-  pl_alabel_r(ch->lp,0,'t',label);
-
-  pl_restorestate_r(ch->lp);
-
+  cairo_move_to (cr, geom->data_left, geom->abscissa_top);
+  chart_label (cr, 'l', 't', geom->font_size, label);
 }
 
-
-
 /* Write the ordinate label */
 void
-chart_write_ylabel(struct chart *ch, const char *label)
+chart_write_ylabel (cairo_t *cr, const struct chart_geometry *geom,
+                    const char *label)
 {
-  if ( ! ch )
-    return ;
-
-  pl_savestate_r(ch->lp);
-
-  pl_move_r(ch->lp, ch->data_bottom, ch->ordinate_right);
-  pl_textangle_r(ch->lp, 90);
-  pl_alabel_r(ch->lp, 0, 0, label);
-
-  pl_restorestate_r(ch->lp);
+  cairo_save (cr);
+  cairo_translate (cr, -geom->data_bottom, -geom->ordinate_right);
+  cairo_move_to (cr, 0, 0);
+  cairo_rotate (cr, M_PI / 2.0);
+  chart_label (cr, 'l', 'x', geom->font_size, label);
+  cairo_restore (cr);
 }
 
 
 void
-chart_write_legend (struct chart *ch)
+chart_write_legend (cairo_t *cr, const struct chart_geometry *geom)
 {
   int i;
-  const int vstep = ch->font_size * 2;
+  const int vstep = geom->font_size * 2;
   const int xpad = 10;
   const int ypad = 10;
   const int swatch = 20;
-  const int legend_top = ch->data_top;
+  const int legend_top = geom->data_top;
   const int legend_bottom = legend_top -
-    (vstep * ch->n_datasets + 2 * ypad );
-
-  if ( ! ch )
-    return ;
+    (vstep * geom->n_datasets + 2 * ypad );
 
-  pl_savestate_r (ch->lp);
+  cairo_save (cr);
 
-  pl_box_r (ch->lp, ch->legend_left, legend_top,
-           ch->legend_right - xpad, legend_bottom);
+  cairo_rectangle (cr, geom->legend_left, legend_top,
+                   geom->legend_right - xpad - geom->legend_left,
+                   legend_bottom - legend_top);
+  cairo_stroke (cr);
 
-  for (i = 0 ; i < ch->n_datasets ; ++i )
+  for (i = 0 ; i < geom->n_datasets ; ++i )
     {
-      const int ypos = vstep * (i + 1);
-      const int xpos = ch->legend_left + xpad;
-      pl_move_r (ch->lp, xpos, legend_top - ypos);
-
-      pl_savestate_r(ch->lp);
-       pl_fillcolorname_r (ch->lp, data_colour [ i % N_CHART_COLOURS]);
-
-       pl_pentype_r (ch->lp, 1);
-       pl_filltype_r (ch->lp, 1);
-       pl_boxrel_r (ch->lp, 0, 0, swatch, swatch);
-
-
-      pl_moverel_r (ch->lp, swatch, 0);
-      pl_alabel_r (ch->lp, 0, 0, ch->dataset[i]);
-
-      pl_restorestate_r (ch->lp);
+      const int ypos = legend_top - vstep * (i + 1);
+      const int xpos = geom->legend_left + xpad;
+      const struct chart_colour *colour;
+
+      cairo_move_to (cr, xpos, ypos);
+
+      cairo_save (cr);
+      colour = &data_colour [ i % N_CHART_COLOURS];
+      cairo_set_source_rgb (cr,
+                            colour->red / 255.0,
+                            colour->green / 255.0,
+                            colour->blue / 255.0);
+      cairo_rectangle (cr, xpos, ypos, swatch, swatch);
+      cairo_fill_preserve (cr);
+      cairo_stroke (cr);
+      cairo_restore (cr);
+
+      cairo_move_to (cr, xpos + swatch * 1.5, ypos);
+      chart_label (cr, 'l', 'x', geom->font_size, geom->dataset[i]);
     }
 
-  pl_restorestate_r (ch->lp);
+  cairo_restore (cr);
 }
index cfbaa4df8ddc396ee4d2338dee87acfd349bf0a5..896b630b137e7245f679c15c5437e7b8a47168fe 100644 (file)
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
+#ifndef PLOT_CHART_H
+#define PLOT_CHART_H
+
+#include <cairo/cairo.h>
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
 
 #include <math/chart-geometry.h>
 #include <output/chart.h>
+#include <output/chart-provider.h>
 
 #include <libpspp/compiler.h>
 #include <libpspp/str.h>
 #include <output/manager.h>
 #include <output/output.h>
 
-#include "xalloc.h"
-
-#ifndef PLOT_CHART_H
-#define PLOT_CHART_H
-
 #define N_CHART_COLOURS 9
-extern const char *const data_colour[];
+extern const struct chart_colour data_colour[];
 
 enum tick_orientation
   {
@@ -45,34 +45,54 @@ enum tick_orientation
     TICK_ORDINATE
   };
 
+struct chart_geometry;
+
+
+enum marker_type
+  {
+    MARKER_CIRCLE,              /* Hollow circle. */
+    MARKER_ASTERISK,            /* Asterisk (*). */
+    MARKER_SQUARE               /* Hollow square. */
+  };
+
+void chart_draw_marker (cairo_t *, double x, double y, enum marker_type,
+                        double size);
+
+void chart_label (cairo_t *, int horz_justify, int vert_justify,
+                  double font_size, const char *);
 
 /* Draw a tick mark at position
    If label is non zero, then print it at the tick mark
 */
-void draw_tick(struct chart *chart,
+void draw_tick(cairo_t *, const struct chart_geometry *,
          enum tick_orientation orientation,
          double position,
               const char *label, ...)
-  PRINTF_FORMAT (4, 5);
+  PRINTF_FORMAT (5, 6);
 
 
 /* Write the title on a chart*/
-void   chart_write_title(struct chart *chart, const char *title, ...)
-  PRINTF_FORMAT (2, 3);
+void   chart_write_title(cairo_t *, const struct chart_geometry *,
+                         const char *title, ...)
+  PRINTF_FORMAT (3, 4);
 
 
 /* Set the scale for the abscissa */
-void  chart_write_xscale(struct chart *ch, double min, double max, int ticks);
+void  chart_write_xscale(cairo_t *, struct chart_geometry *,
+                         double min, double max, int ticks);
 
 
 /* Set the scale for the ordinate */
-void  chart_write_yscale(struct chart *ch, double smin, double smax, int ticks);
+void  chart_write_yscale(cairo_t *, struct chart_geometry *,
+                         double smin, double smax, int ticks);
 
-void chart_write_xlabel(struct chart *ch, const char *label) ;
+void chart_write_xlabel(cairo_t *, const struct chart_geometry *,
+                        const char *label) ;
 
 /* Write the ordinate label */
-void  chart_write_ylabel(struct chart *ch, const char *label);
+void  chart_write_ylabel(cairo_t *, const struct chart_geometry *,
+                         const char *label);
 
-void chart_write_legend (struct chart *ch);
+void chart_write_legend (cairo_t *, const struct chart_geometry *);
 
 #endif
index fbf1925e635e165c3c096d119bace8123fa0d5bc..334818b4cad9428975f8c2e1623ea567ff57ac5c 100644 (file)
@@ -18,7 +18,6 @@
 #include <config.h>
 
 #include <stdio.h>
-#include <plot.h>
 #include <math.h>
 #include <gsl/gsl_histogram.h>
 #include <gsl/gsl_randist.h>
 
 #include <output/charts/plot-hist.h>
 #include <output/charts/plot-chart.h>
+#include <output/chart-provider.h>
 
 #include <data/variable.h>
+#include <libpspp/cast.h>
 #include <libpspp/hash.h>
 #include <output/chart.h>
 #include <math/histogram.h>
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
+static const struct chart_class histogram_chart_class;
+
 /* Write the legend of the chart */
 static void
-histogram_write_legend (struct chart *ch, double n, double mean, double stddev)
+histogram_write_legend (cairo_t *cr, const struct chart_geometry *geom,
+                        double n, double mean, double stddev)
 {
-  char buf[100];
-
-  if (!ch)
-    return ;
-
-  pl_savestate_r (ch->lp);
+  double y = geom->data_bottom;
+  cairo_save (cr);
 
-  sprintf (buf, "N = %.2f", n);
-  pl_move_r (ch->lp, ch->legend_left, ch->data_bottom);
-  pl_alabel_r (ch->lp, 0, 'b', buf);
+  if (n != SYSMIS)
+    {
+      char *buf = xasprintf ("N = %.2f", n);
+      cairo_move_to (cr, geom->legend_left, y);
+      chart_label (cr, 'l', 'b', geom->font_size, buf);
+      y += geom->font_size * 1.5;
+      free (buf);
+    }
 
-  sprintf (buf, "Mean = %.1f", mean);
-  pl_fmove_r (ch->lp,ch->legend_left,ch->data_bottom + ch->font_size * 1.5);
-  pl_alabel_r (ch->lp, 0, 'b', buf);
+  if (mean != SYSMIS)
+    {
+      char *buf = xasprintf ("Mean = %.1f", mean);
+      cairo_move_to (cr,geom->legend_left, y);
+      chart_label (cr, 'l', 'b', geom->font_size, buf);
+      y += geom->font_size * 1.5;
+      free (buf);
+    }
 
-  sprintf (buf, "Std. Dev = %.2f", stddev);
-  pl_fmove_r (ch->lp, ch->legend_left, ch->data_bottom + ch->font_size * 1.5 * 2);
-  pl_alabel_r (ch->lp, 0, 'b', buf);
+  if (stddev != SYSMIS)
+    {
+      char *buf = xasprintf ("Std. Dev = %.2f", stddev);
+      cairo_move_to (cr, geom->legend_left, y);
+      chart_label (cr, 'l', 'b', geom->font_size, buf);
+      free (buf);
+    }
 
-  pl_restorestate_r (ch->lp);
+  cairo_restore (cr);
 }
 
-static void hist_draw_bar (struct chart *ch, const struct histogram *hist, int bar);
-
-
 static void
-hist_draw_bar (struct chart *ch, const struct histogram *hist, int bar)
+hist_draw_bar (cairo_t *cr, const struct chart_geometry *geom,
+               const gsl_histogram *h, int bar)
 {
-  if (!ch)
-    return ;
-
-  {
-    double upper;
-    double lower;
-    double height;
-
-    const size_t bins = gsl_histogram_bins (hist->gsl_hist);
-    const double x_pos = (ch->data_right - ch->data_left) * bar / (double) bins ;
-    const double width = (ch->data_right - ch->data_left) / (double) bins ;
-
-    assert ( 0 == gsl_histogram_get_range (hist->gsl_hist, bar, &lower, &upper));
-
-    assert ( upper >= lower);
-
-    height = gsl_histogram_get (hist->gsl_hist, bar) *
-     (ch->data_top - ch->data_bottom) / gsl_histogram_max_val (hist->gsl_hist);
-
-    pl_savestate_r (ch->lp);
-    pl_move_r (ch->lp,ch->data_left, ch->data_bottom);
-    pl_fillcolorname_r (ch->lp, ch->fill_colour);
-    pl_filltype_r (ch->lp,1);
-
-
-    pl_fboxrel_r (ch->lp,
-                x_pos, 0,
-                x_pos + width, height);
-
-    pl_restorestate_r (ch->lp);
-
-    draw_tick (ch, TICK_ABSCISSA,
-               x_pos + width / 2.0, "%g", (upper + lower) / 2.0);
-  }
+  double upper;
+  double lower;
+  double height;
+
+  const size_t bins = gsl_histogram_bins (h);
+  const double x_pos = (geom->data_right - geom->data_left) * bar / (double) bins ;
+  const double width = (geom->data_right - geom->data_left) / (double) bins ;
+
+  assert ( 0 == gsl_histogram_get_range (h, bar, &lower, &upper));
+
+  assert ( upper >= lower);
+
+  height = gsl_histogram_get (h, bar) *
+    (geom->data_top - geom->data_bottom) / gsl_histogram_max_val (h);
+
+  cairo_rectangle (cr, geom->data_left + x_pos, geom->data_bottom,
+                   width, height);
+  cairo_save (cr);
+  cairo_set_source_rgb (cr,
+                        geom->fill_colour.red / 255.0,
+                        geom->fill_colour.green / 255.0,
+                        geom->fill_colour.blue / 255.0);
+  cairo_fill_preserve (cr);
+  cairo_restore (cr);
+  cairo_stroke (cr);
+
+  draw_tick (cr, geom, TICK_ABSCISSA,
+             x_pos + width / 2.0, "%g", (upper + lower) / 2.0);
 }
 
-
-
-void
-histogram_plot (const struct histogram *hist,
-               const char *label,
-               const struct moments1 *m)
+struct histogram_chart
+  {
+    struct chart chart;
+    gsl_histogram *gsl_hist;
+    char *label;
+    double n;
+    double mean;
+    double stddev;
+    bool show_normal;
+  };
+
+/* Plots a histogram of the data in HIST with the given LABEL.
+   Labels the histogram with each of N, MEAN, and STDDEV that is
+   not SYSMIS.  If all three are not SYSMIS and SHOW_NORMAL is
+   true, also draws a normal curve on the histogram. */
+struct chart *
+histogram_chart_create (const struct histogram *hist, const char *label,
+                        double n, double mean, double stddev,
+                        bool show_normal)
 {
-  double mean, var, n;
-
-  moments1_calculate (m, &n, &mean, &var, NULL,  NULL);
-
-  histogram_plot_n (hist, label, n, mean, sqrt(var), m);
+  struct histogram_chart *h;
+
+  h = xmalloc (sizeof *h);
+  chart_init (&h->chart, &histogram_chart_class);
+  h->gsl_hist = hist->gsl_hist ? gsl_histogram_clone (hist->gsl_hist) : NULL;
+  h->label = xstrdup (label);
+  h->n = n;
+  h->mean = mean;
+  h->stddev = stddev;
+  h->show_normal = show_normal;
+  return &h->chart;
 }
 
-
-/* This function is deprecated.  Don't use it in new code */
-void
-histogram_plot_n (const struct histogram *hist,
-                 const char *label,
-                 double n, double mean, double stddev,
-                 bool show_normal)
+static void
+histogram_chart_draw (const struct chart *chart, cairo_t *cr,
+                      struct chart_geometry *geom)
 {
+  struct histogram_chart *h = UP_CAST (chart, struct histogram_chart, chart);
   int i;
   int bins;
 
-  struct chart *ch = chart_create ();
-
-  chart_write_title (ch, _("HISTOGRAM"));
+  chart_write_title (cr, geom, _("HISTOGRAM"));
 
-  chart_write_ylabel (ch, _("Frequency"));
-  chart_write_xlabel (ch, label);
+  chart_write_ylabel (cr, geom, _("Frequency"));
+  chart_write_xlabel (cr, geom, h->label);
 
-  if ( ! hist ) /* If this happens, probably all values are SYSMIS */
+  if (h->gsl_hist == NULL)
     {
-      chart_submit (ch);
+      /* Probably all values are SYSMIS. */
       return;
     }
-  else
-    {
-      bins = gsl_histogram_bins (hist->gsl_hist);
-    }
 
-  chart_write_yscale (ch, 0, gsl_histogram_max_val (hist->gsl_hist), 5);
+  bins = gsl_histogram_bins (h->gsl_hist);
+
+  chart_write_yscale (cr, geom, 0, gsl_histogram_max_val (h->gsl_hist), 5);
 
-  for ( i = 0 ; i < bins ; ++i )
-    hist_draw_bar (ch, hist, i);
+  for (i = 0; i < bins; i++)
+    hist_draw_bar (cr, geom, h->gsl_hist, i);
 
-  histogram_write_legend (ch, n, mean, stddev);
+  histogram_write_legend (cr, geom, h->n, h->mean, h->stddev);
 
-  if (show_normal)
+  if (h->show_normal
+      && h->n != SYSMIS && h->mean != SYSMIS && h->stddev != SYSMIS)
     {
       /* Draw the normal curve */
+      double d;
+      double x_min, x_max, not_used;
+      double abscissa_scale;
+      double ordinate_scale;
+      double range;
 
-      double d ;
-      double x_min, x_max, not_used ;
-      double abscissa_scale ;
-      double ordinate_scale ;
-      double range ;
-
-      gsl_histogram_get_range (hist->gsl_hist, 0, &x_min, &not_used);
+      gsl_histogram_get_range (h->gsl_hist, 0, &x_min, &not_used);
       range = not_used - x_min;
-      gsl_histogram_get_range (hist->gsl_hist, bins - 1, &not_used, &x_max);
+      gsl_histogram_get_range (h->gsl_hist, bins - 1, &not_used, &x_max);
 
-      abscissa_scale = (ch->data_right - ch->data_left) / (x_max - x_min);
-      ordinate_scale = (ch->data_top - ch->data_bottom) /
-       gsl_histogram_max_val (hist->gsl_hist) ;
+      abscissa_scale = (geom->data_right - geom->data_left) / (x_max - x_min);
+      ordinate_scale = (geom->data_top - geom->data_bottom) /
+       gsl_histogram_max_val (h->gsl_hist);
 
-      pl_move_r (ch->lp, ch->data_left, ch->data_bottom);
-      for ( d = ch->data_left;
-           d <= ch->data_right ;
-           d += (ch->data_right - ch->data_left) / 100.0)
+      cairo_move_to (cr, geom->data_left, geom->data_bottom);
+      for (d = geom->data_left;
+          d <= geom->data_right;
+          d += (geom->data_right - geom->data_left) / 100.0)
        {
-         const double x = (d - ch->data_left) / abscissa_scale + x_min ;
-         const double y = n * range *
-           gsl_ran_gaussian_pdf (x - mean, stddev);
+         const double x = (d - geom->data_left) / abscissa_scale + x_min;
+         const double y = h->n * range *
+           gsl_ran_gaussian_pdf (x - h->mean, h->stddev);
 
-         pl_fcont_r (ch->lp,  d,  ch->data_bottom  + y * ordinate_scale);
+          cairo_line_to (cr, d, geom->data_bottom  + y * ordinate_scale);
 
        }
-      pl_endpath_r (ch->lp);
+      cairo_stroke (cr);
     }
-
-  chart_submit (ch);
 }
 
 
+static void
+histogram_chart_destroy (struct chart *chart)
+{
+  struct histogram_chart *h = UP_CAST (chart, struct histogram_chart, chart);
+  if (h->gsl_hist != NULL)
+    gsl_histogram_free (h->gsl_hist);
+  free (h->label);
+  free (h);
+}
+
+static const struct chart_class histogram_chart_class =
+  {
+    histogram_chart_draw,
+    histogram_chart_destroy
+  };
index 606206d5012c47d4e7b239e0a0230bf1788cea8e..1e5b59edb5d8458badf707b3f6c21a424afbc3c2 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 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
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-#ifndef PLOT_HIST_H
-#define PLOT_HIST_H
+#ifndef OUTPUT_PLOT_HIST_H
+#define OUTPUT_PLOT_HIST_H
 
 #include <stdbool.h>
 
 struct chart;
-struct moments1;
 struct histogram;
 
-/* Plot M onto histogram HIST and label it with LABEL */
-void histogram_plot (const struct histogram *hist,
-                    const char *label,  const struct moments1 *m);
-
-
-/* A wrapper aroud histogram_plot.
-   Don't use this function.  It's legacy only */
-void histogram_plot_n (const struct histogram *hist,
-                      const char *label,
-                      double n, double mean, double var,
-                      bool show_normal);
-
-
-#endif
+/* Creates and returns a new chart that depicts a histogram of
+   the data in HIST with the given LABEL.  Labels the histogram
+   with each of N, MEAN, and STDDEV that is not SYSMIS.  If all
+   three are not SYSMIS and SHOW_NORMAL is true, also draws a
+   normal curve on the histogram. */
+struct chart *histogram_chart_create (const struct histogram *hist,
+                                      const char *label,
+                                      double n, double mean, double stddev,
+                                      bool show_normal);
+
+#endif /* output/plot-hist.h */
diff --git a/src/output/charts/roc-chart.c b/src/output/charts/roc-chart.c
new file mode 100644 (file)
index 0000000..2094ede
--- /dev/null
@@ -0,0 +1,148 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/charts/roc-chart.h>
+
+#include <output/chart-provider.h>
+#include <output/charts/cartesian.h>
+#include <output/charts/plot-chart.h>
+#include <data/casereader.h>
+#include <language/stats/roc.h>
+
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+struct roc_var
+  {
+    char *name;
+    struct casereader *cutpoint_reader;
+  };
+
+struct roc_chart
+  {
+    struct chart chart;
+    bool reference;
+    struct roc_var *vars;
+    size_t n_vars;
+    size_t allocated_vars;
+  };
+
+static const struct chart_class roc_chart_class;
+
+struct roc_chart *
+roc_chart_create (bool reference)
+{
+  struct roc_chart *rc = xmalloc (sizeof *rc);
+  chart_init (&rc->chart, &roc_chart_class);
+  rc->reference = reference;
+  rc->vars = NULL;
+  rc->n_vars = 0;
+  rc->allocated_vars = 0;
+  return rc;
+}
+
+void
+roc_chart_add_var (struct roc_chart *rc, const char *var_name,
+                   const struct casereader *cutpoint_reader)
+{
+  struct roc_var *rv;
+
+  if (rc->n_vars >= rc->allocated_vars)
+    rc->vars = x2nrealloc (rc->vars, &rc->allocated_vars, sizeof *rc->vars);
+
+  rv = &rc->vars[rc->n_vars++];
+  rv->name = xstrdup (var_name);
+  rv->cutpoint_reader = casereader_clone (cutpoint_reader);
+}
+
+struct chart *
+roc_chart_get_chart (struct roc_chart *rc)
+{
+  return &rc->chart;
+}
+
+static void
+roc_chart_draw (const struct chart *chart, cairo_t *cr,
+                struct chart_geometry *geom)
+{
+  const struct roc_chart *rc = UP_CAST (chart, struct roc_chart, chart);
+  size_t i;
+
+  chart_write_title (cr, geom, _("ROC Curve"));
+  chart_write_xlabel (cr, geom, _("1 - Specificity"));
+  chart_write_ylabel (cr, geom, _("Sensitivity"));
+
+  chart_write_xscale (cr, geom, 0, 1, 5);
+  chart_write_yscale (cr, geom, 0, 1, 5);
+
+  if ( rc->reference )
+    {
+      chart_line (cr, geom, 1.0, 0,
+                 0.0, 1.0,
+                 CHART_DIM_X);
+    }
+
+  for (i = 0; i < rc->n_vars; ++i)
+    {
+      const struct roc_var *rv = &rc->vars[i];
+      struct casereader *r = casereader_clone (rv->cutpoint_reader);
+      struct ccase *cc;
+
+      chart_vector_start (cr, geom, rv->name);
+      for (; (cc = casereader_read (r)) != NULL; case_unref (cc))
+       {
+         double se = case_data_idx (cc, ROC_TP)->f;
+         double sp = case_data_idx (cc, ROC_TN)->f;
+
+         se /= case_data_idx (cc, ROC_FN)->f + case_data_idx (cc, ROC_TP)->f ;
+         sp /= case_data_idx (cc, ROC_TN)->f + case_data_idx (cc, ROC_FP)->f ;
+
+         chart_vector (cr, geom, 1 - sp, se);
+       }
+      chart_vector_end (cr, geom);
+      casereader_destroy (r);
+    }
+
+  chart_write_legend (cr, geom);
+}
+
+static void
+roc_chart_destroy (struct chart *chart)
+{
+  struct roc_chart *rc = UP_CAST (chart, struct roc_chart, chart);
+  size_t i;
+
+  for (i = 0; i < rc->n_vars; i++)
+    {
+      struct roc_var *rv = &rc->vars[i];
+      free (rv->name);
+      casereader_destroy (rv->cutpoint_reader);
+    }
+  free (rc->vars);
+  free (rc);
+}
+
+static const struct chart_class roc_chart_class =
+  {
+    roc_chart_draw,
+    roc_chart_destroy
+  };
+
+
diff --git a/src/output/charts/roc-chart.h b/src/output/charts/roc-chart.h
new file mode 100644 (file)
index 0000000..dca8420
--- /dev/null
@@ -0,0 +1,29 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CHARTS_ROC_CHART_H
+#define OUTPUT_CHARTS_ROC_CHART_H 1
+
+#include <stdbool.h>
+
+struct casereader;
+
+struct roc_chart *roc_chart_create (bool reference);
+void roc_chart_add_var (struct roc_chart *, const char *var_name,
+                        const struct casereader *cutpoint_reader);
+struct chart *roc_chart_get_chart (struct roc_chart *);
+
+#endif /* output/charts/roc-chart.h */
index 893c18527b7ff30e431762ede6658332e6fc5e46..dfa524a7cfd5f60cd6120023283d1106c635c35a 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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
 #include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
 #include <data/file-name.h>
-#include "error.h"
-#include "output.h"
-#include "manager.h"
-#include "table.h"
+#include <output/chart-provider.h>
+#include <output/output.h>
+#include <output/manager.h>
+#include <output/table.h>
 #include <libpspp/version.h>
 
+#include "error.h"
 #include "xalloc.h"
 
 #include "gettext.h"
 static void escape_string (FILE *file,
                            const char *text, size_t length,
                            const char *space);
-static bool handle_option (struct outp_driver *this,
+static bool handle_option (void *this,
                            const char *key, const struct string *val);
 static void print_title_tag (FILE *file, const char *name,
                              const char *content);
 
 static bool
-html_open_driver (struct outp_driver *this, struct substring options)
+html_open_driver (const char *name, int types, struct substring options)
 {
+  struct outp_driver *this;
   struct html_driver_ext *x;
 
+  this = outp_allocate_driver (&html_class, name, types);
   this->ext = x = xmalloc (sizeof *x);
   x->file_name = xstrdup ("pspp.html");
   x->chart_file_name = xstrdup ("pspp-#.png");
   x->file = NULL;
-  x->chart_cnt = 0;
+  x->chart_cnt = 1;
 
-  outp_parse_options (options, handle_option, this);
+  outp_parse_options (name, options, handle_option, this);
 
   x->file = fn_open (x->file_name, "w");
   if (x->file == NULL)
@@ -89,10 +92,12 @@ html_open_driver (struct outp_driver *this, struct substring options)
   print_title_tag (x->file, "H1", outp_title);
   print_title_tag (x->file, "H2", outp_subtitle);
 
+  outp_register_driver (this);
   return true;
 
  error:
   this->class->close_driver (this);
+  outp_free_driver (this);
   return false;
 }
 
@@ -133,14 +138,6 @@ html_close_driver (struct outp_driver *this)
   return ok;
 }
 
-/* Link the image contained in FILE_NAME to the
-   HTML stream in FILE. */
-static void
-link_image (FILE *file, char *file_name)
-{
-  fprintf (file, "<IMG SRC=\"%s\"/>", file_name);
- }
-
 /* Generic option types. */
 enum
   {
@@ -157,9 +154,9 @@ static const struct outp_option option_tab[] =
   };
 
 static bool
-handle_option (struct outp_driver *this,
-               const char *key, const struct string *val)
+handle_option (void *this_, const char *key, const struct string *val)
 {
+  struct outp_driver *this = this_;
   struct html_driver_ext *x = this->ext;
   int subcat;
 
@@ -199,11 +196,21 @@ handle_option (struct outp_driver *this,
 
 static void output_tab_table (struct outp_driver *, struct tab_table *);
 
+static void
+html_output_chart (struct outp_driver *this, const struct chart *chart)
+{
+  struct html_driver_ext *x = this->ext;
+  char *file_name;
+
+  file_name = chart_draw_png (chart, x->chart_file_name, x->chart_cnt++);
+  fprintf (x->file, "<IMG SRC=\"%s\"/>", file_name);
+  free (file_name);
+}
+
 static void
 html_submit (struct outp_driver *this, struct som_entity *s)
 {
   extern struct som_table_class tab_table_class;
-  struct html_driver_ext *x = this->ext;
 
   assert (s->class == &tab_table_class ) ;
 
@@ -212,9 +219,6 @@ html_submit (struct outp_driver *this, struct som_entity *s)
     case SOM_TABLE:
       output_tab_table ( this, (struct tab_table *) s->ext);
       break;
-    case SOM_CHART:
-      link_image (x->file, ((struct chart *)s->ext)->file_name);
-      break;
     default:
       NOT_REACHED ();
     }
@@ -289,7 +293,7 @@ output_tab_table (struct outp_driver *this, struct tab_table *t)
 {
   struct html_driver_ext *x = this->ext;
 
-  if (t->nr == 1 && t->nc == 1)
+  if (tab_nr (t) == 1 && tab_nc (t) == 1)
     {
       fputs ("<P>", x->file);
       html_put_cell_contents (this, t->ct[0], *t->cc);
@@ -311,18 +315,18 @@ output_tab_table (struct outp_driver *this, struct tab_table *t)
     int r;
     unsigned char *ct = t->ct;
 
-    for (r = 0; r < t->nr; r++)
+    for (r = 0; r < tab_nr (t); r++)
       {
        int c;
 
        fputs ("  <TR>\n", x->file);
-       for (c = 0; c < t->nc; c++, ct++)
+       for (c = 0; c < tab_nc (t); c++, ct++)
          {
             struct substring *cc;
             const char *tag;
             struct tab_joined_cell *j = NULL;
 
-            cc = t->cc + c + r * t->nc;
+            cc = t->cc + c + r * tab_nc (t);
            if (*ct & TAB_JOIN)
               {
                 j = (struct tab_joined_cell *) ss_data (*cc);
@@ -332,8 +336,8 @@ output_tab_table (struct outp_driver *this, struct tab_table *t)
               }
 
             /* Output <TD> or <TH> tag. */
-            tag = (r < t->t || r >= t->nr - t->b
-                   || c < t->l || c >= t->nc - t->r) ? "TH" : "TD";
+            tag = (r < tab_t (t) || r >= tab_nr (t) - tab_b (t)
+                   || c < tab_l (t) || c >= tab_nc (t) - tab_r (t)) ? "TH" : "TD";
             fprintf (x->file, "    <%s ALIGN=%s",
                      tag,
                      (*ct & TAB_ALIGN_MASK) == TAB_LEFT ? "LEFT"
@@ -361,19 +365,6 @@ output_tab_table (struct outp_driver *this, struct tab_table *t)
   fputs ("</TABLE>\n\n", x->file);
 }
 
-static void
-html_initialise_chart (struct outp_driver *this UNUSED, struct chart *ch)
-{
-  struct html_driver_ext *x = this->ext;
-  chart_init_separate (ch, "png", x->chart_file_name, ++x->chart_cnt);
-}
-
-static void
-html_finalise_chart(struct outp_driver *d UNUSED, struct chart *ch)
-{
-  chart_finalise_separate (ch);
-}
-
 
 
 /* HTML driver class. */
@@ -389,11 +380,11 @@ const struct outp_class html_class =
     NULL,
     NULL,
 
+    html_output_chart,
+
     html_submit,
 
     NULL,
     NULL,
     NULL,
-    html_initialise_chart,
-    html_finalise_chart
   };
index 0b2fdfe1cf2ab53fc1fdaaf4ce3decf23b14f0ad..9b9013933ae87ed3fde10e6d93e83441a41a8ad2 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+   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
    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 <libpspp/assertion.h>
-#include "output.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
@@ -37,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)
@@ -68,30 +103,16 @@ som_blank_line (void)
       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 *);
 
@@ -99,84 +120,132 @@ 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);
+         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);
+    }
+
+  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
+    output_entity (d, t);
 
-      t->class->columns (&cs);
+#if DEBUGGING
+  assert (--entry == 0);
+#endif
+}
 
-      if (!(flags & SOMF_NO_TITLE))
-       subtable_num++;
+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;
     }
 
-  {
-    struct outp_driver *d;
+  return true;
+}
 
-    for (d = outp_drivers (NULL); d; d = outp_drivers (d))
-       output_entity (d, t);
+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;
+    }
 
-#if DEBUGGING
-  assert (--entry == 0);
-#endif
+  return true;
 }
 
-/* Output entity ENTITY to driver DRIVER. */
+/* 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->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);
 
-  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);
+  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;
@@ -184,22 +253,29 @@ output_entity (struct outp_driver *driver, struct som_entity *entity)
   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 ();
+    render_simple (r, d, t, tw, th, hl, hr, ht, hb);
   else
-    render_segments ();
+    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);
@@ -208,7 +284,7 @@ render_columns (void)
     {
       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)
        {
@@ -220,8 +296,9 @@ 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)
@@ -242,33 +319,44 @@ 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++;
 
@@ -279,7 +367,7 @@ render_segments (void)
          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++;
 
@@ -290,9 +378,10 @@ render_segments (void)
            }
           else
             {
-             t->class->title (x_index ? x_index : y_index,
-                              x_index ? y_index : 0);
-             t->class->render (x0, y0, x1, y1);
+             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;
            }
index 58d0c12d48594862a7e838b0f8f3ee52b50fa605..e7276982fc5c5a064b89cc8e0af364d43283a38f 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 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
@@ -37,8 +37,7 @@
 
 enum som_type
   {
-    SOM_TABLE,
-    SOM_CHART
+    SOM_TABLE
   } ;
 
 /* Entity (Table or Chart) . */
@@ -46,9 +45,15 @@ struct som_entity
   {
     const struct som_table_class *class;       /* Table class. */
     enum som_type type;                 /* Table or Chart */
-    void *ext;                         /* Owned by */
+    void *ext;                         /* Owned by table or chart class. */
+    int table_num;                      /* Table number. */
+    int subtable_num;                   /* Sub-table number. */
+    char *command_name;                 /* Command that yielded this output. */
   };
 
+struct som_entity *som_entity_clone (struct som_entity *);
+void som_entity_destroy (struct som_entity *);
+
 /* Group styles. */
 enum
   {
@@ -59,8 +64,8 @@ enum
 /* Cumulation types. */
 enum
   {
-    SOM_ROWS, SOM_ROW = SOM_ROWS,      /* Rows. */
-    SOM_COLUMNS, SOM_COLUMN = SOM_COLUMNS      /* Columns. */
+    SOM_ROWS,                   /* Rows. */
+    SOM_COLUMNS                 /* Columns. */
   };
 
 /* Flags. */
@@ -75,40 +80,29 @@ enum
 struct outp_driver;
 struct som_table_class
   {
-    /* Set table, driver. */
-    void (*table) (struct som_entity *);
-    void (*driver) (struct outp_driver *);
-
-    /* Query columns and rows. */
-    void (*count) (int *n_columns, int *n_rows);
-    void (*area) (int *horiz, int *vert);
-    void (*width) (int *columns);
-    void (*height) (int *rows);
-    void (*columns) (int *style);
-    int (*breakable) (int row);                                /* ? */
-    void (*headers) (int *l, int *r, int *t, int *b);
-    void (*join) (int *(column[2]), int *(row[2]));    /* ? */
-    void (*cumulate) (int cumtype, int start, int *end, int max, int *actual);
-    void (*flags) (unsigned *);
-    bool (*fits_width) (int width);
-    bool (*fits_length) (int length);
-
-    /* Set columns and rows. */
-    void (*set_width) (int column, int width);         /* ? */
-    void (*set_height) (int row, int height);          /* ? */
-    void (*set_headers) (int l, int r, int t, int b);
-
-    /* Rendering. */
-    void (*title) (int x, int y);
-    void (*render) (int x1, int y1, int x2, int y2);
+    /* Operations on tables. */
+    void (*count) (struct som_entity *, int *n_columns, int *n_rows);
+    void (*columns) (struct som_entity *, int *style);
+    void (*headers) (struct som_entity *, int *l, int *r, int *t, int *b);
+    void (*flags) (struct som_entity *, unsigned *);
+
+    /* Creating and freeing driver-specific table rendering data. */
+    void *(*render_init) (struct som_entity *, struct outp_driver *,
+                          int l, int r, int t, int b);
+    void (*render_free) (void *);
+
+    /* Rendering operations. */
+    void (*area) (void *, int *horiz, int *vert);
+    void (*cumulate) (void *, int cumtype, int start, int *end,
+                      int max, int *actual);
+    void (*title) (void *, int x, int y, int table_num, int subtable_num,
+                   const char *command_name);
+    void (*render) (void *, int x1, int y1, int x2, int y2);
   };
 
-/* Table indexes. */
-extern int table_num;
-extern int subtable_num;
-
 /* Submission. */
 void som_new_series (void);
+void som_set_command_name (const char *);
 void som_submit (struct som_entity *t);
 
 /* Miscellaneous. */
diff --git a/src/output/odt.c b/src/output/odt.c
new file mode 100644 (file)
index 0000000..8471b6b
--- /dev/null
@@ -0,0 +1,583 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* A driver for creating OpenDocument Format text files from PSPP's output */
+
+#include <libpspp/assertion.h>
+#include <libpspp/version.h>
+
+#include <output/manager.h>
+#include <output/output.h>
+#include <output/table.h>
+
+#include <time.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <libgen.h>
+
+#include <libxml/xmlwriter.h>
+
+#include "xalloc.h"
+
+#include "error.h"
+
+#define _xml(X) (const xmlChar *)(X)
+
+
+struct odf_driver_options
+{
+  struct outp_driver *driver;
+  
+  char *file_name;            /* Output file name. */
+  bool debug;
+};
+
+
+struct odt_driver_ext 
+{
+  /* The name of the temporary directory used to construct the ODF */
+  char *dirname;
+
+  /* Writer for the content.xml file */
+  xmlTextWriterPtr content_wtr;
+
+  /* Writer fot the manifest.xml file */
+  xmlTextWriterPtr manifest_wtr;
+
+  struct odf_driver_options opts;
+};
+
+
+
+/* Create the "mimetype" file needed by ODF */
+static void
+create_mimetype (const char *dirname)
+{
+  FILE *fp;
+  struct string filename;
+  ds_init_cstr (&filename, dirname);
+  ds_put_cstr (&filename, "/mimetype");
+  fp = fopen (ds_cstr (&filename), "w");
+  ds_destroy (&filename);
+
+  assert (fp);
+  fprintf (fp, "application/vnd.oasis.opendocument.text");
+  fclose (fp);
+}
+
+/* Create a new XML file called FILENAME in the temp directory, and return a writer for it */
+static xmlTextWriterPtr
+create_writer (const struct odt_driver_ext *driver, const char *filename)
+{
+  char *copy = NULL;
+  xmlTextWriterPtr w;
+  struct string str;
+  ds_init_cstr (&str, driver->dirname);
+  ds_put_cstr (&str, "/");
+  ds_put_cstr (&str, filename);
+
+  /* dirname modifies its argument, so we must copy it */
+  copy = xstrdup (ds_cstr (&str));
+  mkdir (dirname (copy), 0700);
+  free (copy);
+
+  w = xmlNewTextWriterFilename (ds_cstr (&str), 0);
+
+  ds_destroy (&str);
+
+  xmlTextWriterStartDocument (w, NULL, "UTF-8", NULL);
+
+  return w;
+}
+
+
+static void
+register_file (struct odt_driver_ext *x, const char *filename)
+{
+  assert (x->manifest_wtr);
+  xmlTextWriterStartElement (x->manifest_wtr, _xml("manifest:file-entry"));
+  xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:media-type"),  _xml("text/xml"));
+  xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:full-path"),  _xml (filename));
+  xmlTextWriterEndElement (x->manifest_wtr);
+}
+
+static void
+write_style_data (struct odt_driver_ext *x)
+{
+  xmlTextWriterPtr w = create_writer (x, "styles.xml");
+  register_file (x, "styles.xml");
+
+  xmlTextWriterStartElement (w, _xml ("office:document-styles"));
+  xmlTextWriterWriteAttribute (w, _xml ("xmlns:office"),
+                              _xml ("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+
+  xmlTextWriterWriteAttribute (w, _xml ("xmlns:style"),
+                              _xml ("urn:oasis:names:tc:opendocument:xmlns:style:1.0"));
+
+  xmlTextWriterWriteAttribute (w, _xml ("xmlns:fo"),
+                              _xml ("urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0") );
+
+  xmlTextWriterWriteAttribute (w, _xml ("office:version"),  _xml ("1.1"));
+                              
+
+
+  xmlTextWriterStartElement (w, _xml ("office:styles"));
+
+
+  {
+    xmlTextWriterStartElement (w, _xml ("style:style"));
+    xmlTextWriterWriteAttribute (w, _xml ("style:name"),
+                                _xml ("Standard"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:family"),
+                                _xml ("paragraph"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:class"),
+                                _xml ("text"));
+
+    xmlTextWriterEndElement (w); /* style:style */
+  }
+
+  {
+    xmlTextWriterStartElement (w, _xml ("style:style"));
+    xmlTextWriterWriteAttribute (w, _xml ("style:name"),
+                                _xml ("Table_20_Contents"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:display-name"),
+                                _xml ("Table Contents"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:family"),
+                                _xml ("paragraph"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:parent-style-name"),
+                                _xml ("Standard"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:class"),
+                                _xml ("extra"));
+
+    xmlTextWriterEndElement (w); /* style:style */
+  }
+
+  {
+    xmlTextWriterStartElement (w, _xml ("style:style"));
+    xmlTextWriterWriteAttribute (w, _xml ("style:name"),
+                                _xml ("Table_20_Heading"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:display-name"),
+                                _xml ("Table Heading"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:family"),
+                                _xml ("paragraph"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:parent-style-name"),
+                                _xml ("Table_20_Contents"));
+
+    xmlTextWriterWriteAttribute (w, _xml ("style:class"),
+                                _xml ("extra"));
+
+
+    xmlTextWriterStartElement (w, _xml ("style:text-properties"));
+    xmlTextWriterWriteAttribute (w, _xml ("fo:font-weight"), _xml ("bold"));
+    xmlTextWriterWriteAttribute (w, _xml ("style:font-weight-asian"), _xml ("bold"));
+    xmlTextWriterWriteAttribute (w, _xml ("style:font-weight-complex"), _xml ("bold"));
+    xmlTextWriterEndElement (w); /* style:text-properties */
+
+    xmlTextWriterEndElement (w); /* style:style */
+  }
+
+
+  xmlTextWriterEndElement (w); /* office:styles */
+  xmlTextWriterEndElement (w); /* office:document-styles */
+
+  xmlTextWriterEndDocument (w);
+  xmlFreeTextWriter (w);
+}
+
+static void
+write_meta_data (struct odt_driver_ext *x)
+{
+  xmlTextWriterPtr w = create_writer (x, "meta.xml");
+  register_file (x, "meta.xml");
+
+  xmlTextWriterStartElement (w, _xml ("office:document-meta"));
+  xmlTextWriterWriteAttribute (w, _xml ("xmlns:office"), _xml ("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+  xmlTextWriterWriteAttribute (w, _xml ("xmlns:dc"),  _xml ("http://purl.org/dc/elements/1.1/"));
+  xmlTextWriterWriteAttribute (w, _xml ("xmlns:meta"), _xml ("urn:oasis:names:tc:opendocument:xmlns:meta:1.0"));
+  xmlTextWriterWriteAttribute (w, _xml ("xmlns:ooo"), _xml("http://openoffice.org/2004/office"));
+  xmlTextWriterWriteAttribute (w, _xml ("office:version"),  _xml("1.1"));
+
+  xmlTextWriterStartElement (w, _xml ("office:meta"));
+  {
+    xmlTextWriterStartElement (w, _xml ("meta:generator"));
+    xmlTextWriterWriteString (w, _xml (stat_version));
+    xmlTextWriterEndElement (w);
+  }
+
+
+  {
+    char buf[30];
+    struct passwd *pw = getpwuid (getuid ());
+    time_t t = time (NULL);
+    struct tm *tm =  localtime (&t);
+
+    strftime (buf, 30, "%Y-%m-%dT%H:%M:%S", tm);
+
+    xmlTextWriterStartElement (w, _xml ("meta:initial-creator"));
+    xmlTextWriterWriteString (w, _xml (strtok (pw->pw_gecos, ",")));
+    xmlTextWriterEndElement (w);
+
+    xmlTextWriterStartElement (w, _xml ("meta:creation-date"));
+    xmlTextWriterWriteString (w, _xml (buf));
+    xmlTextWriterEndElement (w);
+
+    xmlTextWriterStartElement (w, _xml ("dc:creator"));
+    xmlTextWriterWriteString (w, _xml (strtok (pw->pw_gecos, ",")));
+
+    xmlTextWriterEndElement (w);
+
+    xmlTextWriterStartElement (w, _xml ("dc:date"));
+    xmlTextWriterWriteString (w, _xml (buf));
+    xmlTextWriterEndElement (w);
+  }
+
+  xmlTextWriterEndElement (w);
+  xmlTextWriterEndElement (w);
+  xmlTextWriterEndDocument (w);
+  xmlFreeTextWriter (w);
+}
+
+enum
+{
+  output_file_arg,
+  boolean_arg,
+};
+
+static const struct outp_option option_tab[] =
+{
+  {"output-file",              output_file_arg,0},
+
+  {"debug",                    boolean_arg,    1},
+
+  {NULL, 0, 0},
+};
+
+static bool
+handle_option (void *options_, const char *key, const struct string *val)
+{
+  struct odf_driver_options *options = options_;
+  struct outp_driver *this = options->driver;
+  int subcat;
+  char *value = ds_cstr (val);
+
+  switch (outp_match_keyword (key, option_tab, &subcat))
+    {
+    case -1:
+      error (0, 0,
+             _("unknown configuration parameter `%s' for %s device "
+               "driver"), key, this->class->name);
+      break;
+    case output_file_arg:
+      free (options->file_name);
+      options->file_name = xstrdup (value);
+      break;
+    case boolean_arg:
+      if (!strcmp (value, "on") || !strcmp (value, "true")
+          || !strcmp (value, "yes") || atoi (value))
+        options->debug = true;
+      else if (!strcmp (value, "off") || !strcmp (value, "false")
+               || !strcmp (value, "no") || !strcmp (value, "0"))
+        options->debug = false;
+      else
+        {
+          error (0, 0, _("boolean value expected for %s"), key);
+          return false;
+        }
+      break;
+
+    default:
+      NOT_REACHED ();
+    }
+
+  return true;
+}
+
+
+static bool
+odt_open_driver (const char *name, int types, struct substring option_string)
+{
+  struct odt_driver_ext *x;
+  struct outp_driver *this = outp_allocate_driver (&odt_class, name, types);
+
+  this->ext = x = xmalloc (sizeof *x);
+
+  x->opts.driver = this;
+  x->opts.file_name = xstrdup ("pspp.pdt");
+  x->opts.debug = false;
+
+  outp_parse_options (this->name, option_string, handle_option, &x->opts);
+
+  outp_register_driver (this);
+
+  x->dirname = xstrdup ("odt-XXXXXX");
+  mkdtemp (x->dirname);
+
+  create_mimetype (x->dirname);
+
+  /* Create the manifest */
+  x->manifest_wtr = create_writer (x, "META-INF/manifest.xml");
+
+  xmlTextWriterStartElement (x->manifest_wtr, _xml("manifest:manifest"));
+  xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("xmlns:manifest"),
+                              _xml("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"));
+
+
+  /* Add a manifest entry for the document as a whole */
+  xmlTextWriterStartElement (x->manifest_wtr, _xml("manifest:file-entry"));
+  xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:media-type"),  _xml("application/vnd.oasis.opendocument.text"));
+  xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:full-path"),  _xml("/"));
+  xmlTextWriterEndElement (x->manifest_wtr);
+
+
+  write_meta_data (x);
+  write_style_data (x);
+
+  x->content_wtr = create_writer (x, "content.xml");
+  register_file (x, "content.xml");
+
+
+  /* Some necessary junk at the start */
+  xmlTextWriterStartElement (x->content_wtr, _xml("office:document-content"));
+  xmlTextWriterWriteAttribute (x->content_wtr, _xml("xmlns:office"),
+                              _xml("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+
+  xmlTextWriterWriteAttribute (x->content_wtr, _xml("xmlns:text"),
+                              _xml("urn:oasis:names:tc:opendocument:xmlns:text:1.0"));
+
+  xmlTextWriterWriteAttribute (x->content_wtr, _xml("xmlns:table"),
+                              _xml("urn:oasis:names:tc:opendocument:xmlns:table:1.0"));
+
+  xmlTextWriterWriteAttribute (x->content_wtr, _xml("office:version"), _xml("1.1"));
+
+  xmlTextWriterStartElement (x->content_wtr, _xml("office:body"));
+  xmlTextWriterStartElement (x->content_wtr, _xml("office:text"));
+
+
+
+  /* Close the manifest */
+  xmlTextWriterEndElement (x->manifest_wtr);
+  xmlTextWriterEndDocument (x->manifest_wtr);
+  xmlFreeTextWriter (x->manifest_wtr);
+
+  return true;
+}
+
+static bool
+odt_close_driver (struct outp_driver *this)
+{
+  struct string zip_cmd;
+  struct string rm_cmd;
+  struct odt_driver_ext *x = this->ext;
+
+  xmlTextWriterEndElement (x->content_wtr); /* office:text */
+  xmlTextWriterEndElement (x->content_wtr); /* office:body */
+  xmlTextWriterEndElement (x->content_wtr); /* office:document-content */
+
+  xmlTextWriterEndDocument (x->content_wtr);
+  xmlFreeTextWriter (x->content_wtr);
+
+  /* Zip up the directory */
+  ds_init_empty (&zip_cmd);
+  ds_put_format (&zip_cmd,
+                "cd %s ; rm -f ../%s; zip -q -X ../%s mimetype; zip -q -X -u -r ../pspp.odt .",
+                x->dirname, x->opts.file_name, x->opts.file_name);
+  system (ds_cstr (&zip_cmd));
+  ds_destroy (&zip_cmd);
+
+
+  if ( !x->opts.debug )
+    {
+      /* Remove the temp dir */
+      ds_init_empty (&rm_cmd);
+      ds_put_format (&rm_cmd, "rm -r %s", x->dirname);
+      system (ds_cstr (&rm_cmd));
+      ds_destroy (&rm_cmd);
+    }
+  else
+    fprintf (stderr, "Not removing directory %s\n", x->dirname);
+
+  free (x->dirname);
+  free (x);
+
+  return true;
+}
+
+static void
+odt_open_page (struct outp_driver *this UNUSED)
+{
+}
+
+static void
+odt_close_page (struct outp_driver *this UNUSED)
+{
+}
+
+static void
+odt_output_chart (struct outp_driver *this UNUSED, const struct chart *chart UNUSED)
+{
+ printf ("%s\n", __FUNCTION__);
+}
+
+
+/* Submit a table to the ODT driver */
+static void
+odt_submit (struct outp_driver *this, struct som_entity *e)
+{
+  int r, c;
+  
+  struct odt_driver_ext *x = this->ext;
+  struct tab_table *tab = e->ext;
+
+
+  /* Write a heading for the table */
+  xmlTextWriterStartElement (x->content_wtr, _xml("text:h"));
+  xmlTextWriterWriteFormatAttribute (x->content_wtr, _xml("text:level"), "%d", e->subtable_num == 1 ? 2 : 3);
+  xmlTextWriterWriteString (x->content_wtr, _xml (tab->title) );
+  xmlTextWriterEndElement (x->content_wtr);
+
+  /* Start table */
+  xmlTextWriterStartElement (x->content_wtr, _xml("table:table"));
+  xmlTextWriterWriteFormatAttribute (x->content_wtr, _xml("table:name"), 
+                                    "TABLE-%d.%d", e->table_num, e->subtable_num);
+
+
+  /* Start column definitions */
+  xmlTextWriterStartElement (x->content_wtr, _xml("table:table-column"));
+  xmlTextWriterWriteFormatAttribute (x->content_wtr, _xml("table:number-columns-repeated"), "%d", tab_nc (tab));
+  xmlTextWriterEndElement (x->content_wtr);
+
+
+  /* Deal with row headers */
+  if ( tab_t (tab) > 0)
+    xmlTextWriterStartElement (x->content_wtr, _xml("table:table-header-rows"));
+    
+
+  /* Write all the rows */
+  for (r = 0 ; r < tab_nr (tab); ++r)
+    {
+      int spanned_columns = 0;
+      /* Start row definition */
+      xmlTextWriterStartElement (x->content_wtr, _xml("table:table-row"));
+
+      /* Write all the columns */
+      for (c = 0 ; c < tab_nc (tab) ; ++c)
+       {
+         char *s = NULL;
+         unsigned int opts = tab->ct[tab_nc (tab) * r + c];
+         struct substring ss = tab->cc[tab_nc (tab) * r + c];
+
+         if (opts & TAB_EMPTY)
+           {
+             xmlTextWriterStartElement (x->content_wtr, _xml("table:table-cell"));
+             xmlTextWriterEndElement (x->content_wtr);
+             continue;
+           }
+
+         if ( opts & TAB_JOIN)
+           {
+             if ( spanned_columns == 0)
+               {
+                 struct tab_joined_cell *j = (struct tab_joined_cell*) ss_data (ss);
+                 s = ss_xstrdup (j->contents);
+               }
+           }
+         else
+           s = ss_xstrdup (ss);
+
+         if ( spanned_columns == 0 )
+           {
+             xmlTextWriterStartElement (x->content_wtr, _xml("table:table-cell"));
+             xmlTextWriterWriteAttribute (x->content_wtr, _xml("office:value-type"), _xml("string"));
+
+             if ( opts & TAB_JOIN )
+               {
+                 struct tab_joined_cell *j = (struct tab_joined_cell*) ss_data (ss);
+                 spanned_columns = j->x2 - j->x1;
+
+                 xmlTextWriterWriteFormatAttribute (x->content_wtr,
+                                                    _xml("table:number-columns-spanned"),
+                                                    "%d", spanned_columns);
+               }
+
+             xmlTextWriterStartElement (x->content_wtr, _xml("text:p"));
+
+             if ( r < tab_t (tab) || c < tab_l (tab) )
+               xmlTextWriterWriteAttribute (x->content_wtr, _xml("text:style-name"), _xml("Table_20_Heading"));
+             else
+               xmlTextWriterWriteAttribute (x->content_wtr, _xml("text:style-name"), _xml("Table_20_Contents"));
+
+             xmlTextWriterWriteString (x->content_wtr, _xml (s));
+         
+             xmlTextWriterEndElement (x->content_wtr); /* text:p */
+             xmlTextWriterEndElement (x->content_wtr); /* table:table-cell */
+           }
+         else
+           {
+             xmlTextWriterStartElement (x->content_wtr, _xml("table:covered-table-cell"));
+             xmlTextWriterEndElement (x->content_wtr);
+           }
+         if ( opts & TAB_JOIN )
+           spanned_columns --;
+
+         free (s);
+       }
+  
+      xmlTextWriterEndElement (x->content_wtr); /* row */
+
+      if ( tab_t (tab) > 0 && r == tab_t (tab) - 1)
+       xmlTextWriterEndElement (x->content_wtr); /* table-header-rows */
+    }
+
+  xmlTextWriterEndElement (x->content_wtr); /* table */
+}
+
+
+/* ODT driver class. */
+const struct outp_class odt_class =
+{
+  "odf",
+  1,
+
+  odt_open_driver,
+  odt_close_driver,
+
+  odt_open_page,
+  odt_close_page,
+  NULL,
+
+  odt_output_chart,
+  odt_submit,
+
+  NULL,
+  NULL,
+  NULL,
+};
index 843b0d4e544996cebcd736b25c8225e185337d1f..a556ad81e8e7b1f49584261829459988b83b732b 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+   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
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-/* FIXME? Should the output configuration format be changed to
-   drivername:classname:devicetype:options, where devicetype is zero
-   or more of screen, printer, listing? */
-
-/* FIXME: Have the reentrancy problems been solved? */
-
 /* Where the output driver name came from. */
 enum
   {
@@ -79,7 +73,7 @@ struct outp_driver_class_list
   };
 
 static struct outp_driver_class_list *outp_class_list;
-static struct outp_driver *outp_driver_list;
+static struct ll_list outp_driver_list = LL_INITIALIZER (outp_driver_list);
 
 char *outp_title;
 char *outp_subtitle;
@@ -228,14 +222,15 @@ find_defn_value (const char *key)
 void
 outp_init (void)
 {
-  extern struct outp_class ascii_class;
-  extern struct outp_class postscript_class;
-
   char def[] = "default";
 
   add_class (&html_class);
   add_class (&postscript_class);
   add_class (&ascii_class);
+#ifdef HAVE_CAIRO
+  add_class (&cairo_class);
+#endif
+  add_class (&odt_class);
 
   add_name (def, &def[strlen (def)], OUTP_S_INIT_FILE);
 }
@@ -345,13 +340,13 @@ exit:
 
   if (result)
     {
-      if (outp_driver_list == NULL)
+      if (ll_is_empty (&outp_driver_list))
         error (0, 0, _("no active output drivers"));
     }
   else
     error (0, 0, _("error reading device definition file"));
 
-  if (!result || outp_driver_list == NULL)
+  if (!result || ll_is_empty (&outp_driver_list))
     init_default_drivers ();
 }
 
@@ -421,29 +416,18 @@ outp_configure_macro (char *bp)
   outp_macros = d;
 }
 
-/* Destroys all the drivers in driver list *DL and sets *DL to
-   NULL. */
-static void
-destroy_list (struct outp_driver ** dl)
-{
-  struct outp_driver *d, *next;
-
-  for (d = *dl; d; d = next)
-    {
-      destroy_driver (d);
-      next = d->next;
-      free (d);
-    }
-  *dl = NULL;
-}
-
 /* Closes all the output drivers. */
 void
 outp_done (void)
 {
   struct outp_driver_class_list *n = outp_class_list ;
   outp_configure_clear ();
-  destroy_list (&outp_driver_list);
+  while (!ll_is_empty (&outp_driver_list))
+    {
+      struct outp_driver *d = ll_data (ll_head (&outp_driver_list),
+                                       struct outp_driver, node);
+      destroy_driver (d);
+    }
 
   while (n)
     {
@@ -611,10 +595,9 @@ get_option_token (struct substring *s, const char *driver_name,
 }
 
 bool
-outp_parse_options (struct substring options,
-                    bool (*callback) (struct outp_driver *, const char *key,
-                                      const struct string *value),
-                    struct outp_driver *driver)
+outp_parse_options (const char *driver_name, struct substring options,
+                    bool (*callback) (void *aux, const char *key,
+                                      const struct string *value), void *aux)
 {
   struct string key = DS_EMPTY_INITIALIZER;
   struct string value = DS_EMPTY_INITIALIZER;
@@ -627,7 +610,7 @@ outp_parse_options (struct substring options,
       if (ss_is_empty (left))
         break;
 
-      if (!get_option_token (&left, driver->name, &key))
+      if (!get_option_token (&left, driver_name, &key))
         break;
 
       ss_ltrim (&left, ss_cstr (CC_SPACES));
@@ -635,15 +618,15 @@ outp_parse_options (struct substring options,
        {
          error (0, 0, _("syntax error expecting `=' "
                          "parsing options for driver \"%s\""),
-                 driver->name);
+                 driver_name);
          break;
        }
 
       ss_ltrim (&left, ss_cstr (CC_SPACES));
-      if (!get_option_token (&left, driver->name, &value))
+      if (!get_option_token (&left, driver_name, &value))
         break;
 
-      ok = callback (driver, ds_cstr (&key), &value);
+      ok = callback (aux, ds_cstr (&key), &value);
     }
   while (ok);
 
@@ -658,8 +641,7 @@ static struct outp_driver *
 find_driver (char *name)
 {
   struct outp_driver *d;
-
-  for (d = outp_driver_list; d; d = d->next)
+  ll_for_each (d, struct outp_driver, node, &outp_driver_list)
     if (!strcmp (d->name, name))
       return d;
   return NULL;
@@ -671,11 +653,10 @@ static void
 configure_driver (struct substring driver_name, struct substring class_name,
                   struct substring device_type, struct substring options)
 {
-  struct outp_driver *d, *iter;
   struct outp_driver_class_list *c;
-
   struct substring token;
   size_t save_idx = 0;
+  char *name;
   int device;
 
   /* Find class. */
@@ -702,38 +683,67 @@ configure_driver (struct substring driver_name, struct substring class_name,
       error (0, 0, _("unknown device type `%.*s'"),
              (int) ss_length (token), ss_data (token));
 
-  /* Open the device. */
-  d = xmalloc (sizeof *d);
-  d->next = d->prev = NULL;
-  d->class = c->class;
-  d->name = ss_xstrdup (driver_name);
+  /* Open driver. */
+  name = ss_xstrdup (driver_name);
+  if (!c->class->open_driver (name, device, options))
+    error (0, 0, _("cannot initialize output driver `%s' of class `%s'"),
+           name, c->class->name);
+  free (name);
+}
+
+/* Allocates and returns a new outp_driver for a device with the
+   given NAME and CLASS and the OUTP_DEV_* type(s) in TYPES
+
+   This function is intended to be used by output drivers, not
+   by their clients. */
+struct outp_driver *
+outp_allocate_driver (const struct outp_class *class,
+                      const char *name, int types)
+{
+  struct outp_driver *d = xmalloc (sizeof *d);
+  d->class = class;
+  d->name = xstrdup (name);
   d->page_open = false;
-  d->device = device;
+  d->device = types;
   d->cp_x = d->cp_y = 0;
   d->ext = NULL;
-  d->prc = NULL;
+  return d;
+}
 
-  /* Open driver. */
-  if (!d->class->open_driver (d, options))
-    {
-      error (0, 0, _("cannot initialize output driver `%s' of class `%s'"),
-             d->name, d->class->name);
-      free (d->name);
-      free (d);
-      return;
-    }
+/* Frees driver D and the data that it owns directly.  The
+   driver's class must already have unregistered D (if it was
+   registered) and freed data private to its class.
+
+   This function is intended to be used by output drivers, not
+   by their clients. */
+void
+outp_free_driver (struct outp_driver *d)
+{
+  free (d->name);
+  free (d);
+}
+
+/* Adds D to the list of drivers that will be used for output. */
+void
+outp_register_driver (struct outp_driver *d)
+{
+  struct outp_driver *victim;
 
   /* Find like-named driver and delete. */
-  iter = find_driver (d->name);
-  if (iter != NULL)
-    destroy_driver (iter);
+  victim = find_driver (d->name);
+  if (victim != NULL)
+    destroy_driver (victim);
 
-  /* Add to list. */
-  d->next = outp_driver_list;
-  d->prev = NULL;
-  if (outp_driver_list != NULL)
-    outp_driver_list->prev = d;
-  outp_driver_list = d;
+  /* Add D to list. */
+  ll_push_tail (&outp_driver_list, &d->node);
+}
+
+/* Remove driver D from the list of drivers that will be used for
+   output. */
+void
+outp_unregister_driver (struct outp_driver *d)
+{
+  ll_remove (&d->node);
 }
 
 /* String LINE is in format:
@@ -772,26 +782,10 @@ static void
 destroy_driver (struct outp_driver *d)
 {
   outp_close_page (d);
-  if (d->class)
-    {
-      struct outp_driver_class_list *c;
-
-      d->class->close_driver (d);
-
-      for (c = outp_class_list; c; c = c->next)
-       if (c->class == d->class)
-         break;
-      assert (c != NULL);
-    }
-  free (d->name);
-
-  /* Remove this driver from the global driver list. */
-  if (d->prev)
-    d->prev->next = d->next;
-  if (d->next)
-    d->next->prev = d->prev;
-  if (d == outp_driver_list)
-    outp_driver_list = d->next;
+  if (d->class && d->class->close_driver)
+    d->class->close_driver (d);
+  outp_unregister_driver (d);
+  outp_free_driver (d);
 }
 
 /* Tries to match S as one of the keywords in TAB, with
@@ -1087,17 +1081,17 @@ outp_get_paper_size (const char *size, int *h, int *v)
 struct outp_driver *
 outp_drivers (struct outp_driver *d)
 {
-  for (;;)
+  do
     {
-      if (d == NULL)
-       d = outp_driver_list;
-      else
-       d = d->next;
+      struct ll *next;
+
+      next = d == NULL ? ll_head (&outp_driver_list) : ll_next (&d->node);
+      if (next == ll_null (&outp_driver_list))
+        return NULL;
 
-      if (d == NULL
-         || (d->device == 0 || (d->device & disabled_devices) != d->device))
-       break;
+      d = ll_data (next, struct outp_driver, node);
     }
+  while (d->device != 0 && (d->device & disabled_devices) == d->device);
 
   return d;
 }
index fc66874068da031cafef1743406131c486ee504d..8ae5c04be32e48c16ba2674fb5bf628b3e9e23b8 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+   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
@@ -17,9 +17,9 @@
 #ifndef OUTPUT_OUTPUT_H
 #define OUTPUT_OUTPUT_H 1
 
+#include <libpspp/ll.h>
 #include <libpspp/str.h>
 
-
 /* Line styles.  */
 enum outp_line_style
   {
@@ -65,7 +65,8 @@ struct outp_class
     const char *name;          /* Name of this driver class. */
     int special;               /* Boolean value. */
 
-    bool (*open_driver) (struct outp_driver *, struct substring options);
+    bool (*open_driver) (const char *name, int types,
+                         struct substring options);
     bool (*close_driver) (struct outp_driver *);
 
     void (*open_page) (struct outp_driver *);
@@ -73,6 +74,8 @@ struct outp_class
 
     void (*flush) (struct outp_driver *);
 
+    void (*output_chart) (struct outp_driver *, const struct chart *);
+
     /* special != 0 only. */
     void (*submit) (struct outp_driver *, struct som_entity *);
 
@@ -83,8 +86,6 @@ struct outp_class
     void (*text_metrics) (struct outp_driver *, const struct outp_text *,
                           int *width, int *height);
     void (*text_draw) (struct outp_driver *, const struct outp_text *);
-    void (*initialise_chart)(struct outp_driver *, struct chart *);
-    void (*finalise_chart)(struct outp_driver *, struct chart *);
   };
 
 /* Device types. */
@@ -99,7 +100,7 @@ enum
 /* Defines the configuration of an output driver. */
 struct outp_driver
   {
-    struct outp_driver *next, *prev; /* List of drivers. */
+    struct ll node;             /* Node in list of drivers. */
     const struct outp_class *class;    /* Driver class. */
     char *name;                        /* Name of this driver. */
     bool page_open;            /* 1=page is open, 0=page is closed. */
@@ -114,7 +115,6 @@ struct outp_driver
     int vert_line_width[OUTP_L_COUNT]; /* Width of vertical lines. */
 
     void *ext;                 /* Private extension record. */
-    void *prc;                 /* Per-procedure extension record. */
   };
 
 /* Option structure for the keyword recognizer. */
@@ -131,9 +131,15 @@ extern char *outp_title;
 extern char *outp_subtitle;
 
 void outp_init (void);
+void outp_done (void);
 void outp_read_devices (void);
 void outp_configure_driver_line (struct substring);
-void outp_done (void);
+
+struct outp_driver *outp_allocate_driver (const struct outp_class *class,
+                                          const char *name, int types);
+void outp_free_driver (struct outp_driver *);
+void outp_register_driver (struct outp_driver *);
+void outp_unregister_driver (struct outp_driver *);
 
 void outp_configure_clear (void);
 void outp_configure_add (char *);
@@ -144,10 +150,10 @@ void outp_list_classes (void);
 void outp_enable_device (bool enable, int device);
 struct outp_driver *outp_drivers (struct outp_driver *);
 
-bool outp_parse_options (struct substring options,
-                         bool (*) (struct outp_driver *, const char *key,
-                                   const struct string *value),
-                         struct outp_driver *);
+bool outp_parse_options (const char *driver_name, struct substring options,
+                         bool (*callback) (void *aux, const char *key,
+                                           const struct string *value),
+                         void *aux);
 int outp_match_keyword (const char *, const struct outp_option *, int *);
 
 int outp_evaluate_dimension (const char *);
@@ -163,4 +169,12 @@ int outp_string_width (struct outp_driver *, const char *, enum outp_font);
 /* Imported from som-frnt.c. */
 void som_destroy_driver (struct outp_driver *);
 
+/* Common drivers. */
+extern const struct outp_class ascii_class;
+extern const struct outp_class postscript_class;
+#ifdef HAVE_CAIRO
+extern const struct outp_class cairo_class;
+#endif
+extern const struct outp_class odt_class;
+
 #endif /* output/output.h */
index 11116b9b6d7a0d3b49db98dda6c7eb455043e5ca..394ebbb27dc5fb3c7d0e251908905db97bd54ddc 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 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
@@ -24,6 +24,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <data/file-name.h>
 #include <libpspp/assertion.h>
 #include <libpspp/bit-vector.h>
 #include <libpspp/compiler.h>
 #include <libpspp/misc.h>
 #include <libpspp/start-date.h>
 #include <libpspp/version.h>
+#include <output/afm.h>
+#include <output/chart-provider.h>
+#include <output/chart.h>
+#include <output/manager.h>
+#include <output/output.h>
 
-#include <data/file-name.h>
-
-#include "afm.h"
-#include "chart.h"
 #include "error.h"
-#include "manager.h"
-#include "output.h"
-
 #include "intprops.h"
 #include "minmax.h"
 #include "xalloc.h"
@@ -105,12 +104,14 @@ struct ps_driver_ext
 
     struct font *fonts[OUTP_FONT_CNT];
     int last_font;              /* Index of last font set with setfont. */
+
+    int doc_num;                /* %%DocumentNumber counter. */
   };
 
 /* Transform logical y-ordinate Y into a page ordinate. */
 #define YT(Y) (this->length - (Y))
 
-static bool handle_option (struct outp_driver *this, const char *key,
+static bool handle_option (void *this, const char *key,
                            const struct string *val);
 static void draw_headers (struct outp_driver *this);
 
@@ -125,11 +126,13 @@ static void setup_font (struct outp_driver *this, struct font *, int index);
 /* Driver initialization. */
 
 static bool
-ps_open_driver (struct outp_driver *this, struct substring options)
+ps_open_driver (const char *name, int types, struct substring options)
 {
+  struct outp_driver *this;
   struct ps_driver_ext *x;
   size_t i;
 
+  this = outp_allocate_driver (&postscript_class, name, types);
   this->width = this->length = 0;
   this->font_height = PSUS * 10 / 72;
 
@@ -149,8 +152,9 @@ ps_open_driver (struct outp_driver *this, struct substring options)
   x->line_width = PSUS / 144;
   for (i = 0; i < OUTP_FONT_CNT; i++)
     x->fonts[i] = NULL;
+  x->doc_num = 0;
 
-  outp_parse_options (options, handle_option, this);
+  outp_parse_options (this->name, options, handle_option, this);
 
   x->file = fn_open (x->file_name, "w");
   if (x->file == NULL)
@@ -219,10 +223,12 @@ ps_open_driver (struct outp_driver *this, struct substring options)
 
   write_ps_prologue (this);
 
+  outp_register_driver (this);
   return true;
 
  error:
   this->class->close_driver (this);
+  outp_free_driver (this);
   return false;
 }
 
@@ -295,9 +301,10 @@ static const struct outp_option option_tab[] =
 };
 
 static bool
-handle_option (struct outp_driver *this, const char *key,
+handle_option (void *this_, const char *key,
                const struct string *val)
 {
+  struct outp_driver *this = this_;
   struct ps_driver_ext *x = this->ext;
   int subcat;
   char *value = ds_cstr (val);
@@ -601,8 +608,6 @@ ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
 {
   switch (s->type)
     {
-    case SOM_CHART:
-      break;
     default:
       NOT_REACHED ();
     }
@@ -1082,72 +1087,6 @@ ps_text_draw (struct outp_driver *this, const struct outp_text *t)
   text (this, t, true, NULL, NULL);
 }
 \f
-static void
-ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch)
-{
-#ifdef NO_CHARTS
-  ch->lp = NULL;
-#else
-  struct ps_driver_ext *x = this->ext;
-  char page_size[128];
-  int size;
-  int x_origin, y_origin;
-
-  ch->file = tmpfile ();
-  if (ch->file == NULL)
-    {
-      ch->lp = NULL;
-      return;
-    }
-
-  size = this->width < this->length ? this->width : this->length;
-  x_origin = x->left_margin + (size - this->width) / 2;
-  y_origin = x->bottom_margin + (size - this->length) / 2;
-
-  snprintf (page_size, sizeof page_size,
-            "a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
-            (double) size / PSUS, (double) size / PSUS,
-            (double) x_origin / PSUS, (double) y_origin / PSUS);
-
-  ch->pl_params = pl_newplparams ();
-  pl_setplparam (ch->pl_params, "PAGESIZE", page_size);
-  ch->lp = pl_newpl_r ("ps", NULL, ch->file, stderr, ch->pl_params);
-#endif
-}
-
-static void
-ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
-{
-#ifndef NO_CHARTS
-  struct ps_driver_ext *x = this->ext;
-  char buf[BUFSIZ];
-  static int doc_num = 0;
-
-  outp_eject_page (this);
-  fprintf (x->file,
-           "/sp save def\n"
-           "%d %d translate 1000 dup scale\n"
-           "userdict begin\n"
-           "/showpage { } def\n"
-           "0 setgray 0 setlinecap 1 setlinewidth\n"
-           "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n"
-           "%%%%BeginDocument: %d\n",
-           -x->left_margin, -x->bottom_margin,
-           doc_num++);
-
-  rewind (ch->file);
-  while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file))
-    continue;
-  fclose (ch->file);
-
-  fputs ("%%EndDocument\n"
-         "end\n"
-         "sp restore\n",
-         x->file);
-  outp_close_page (this);
-#endif
-}
-\f
 static void embed_font (struct outp_driver *this, struct font *font);
 static void reencode_font (struct outp_driver *this, struct font *font);
 
@@ -1435,12 +1374,11 @@ const struct outp_class postscript_class =
   ps_close_page,
   NULL,
 
+  NULL,                         /* output_chart */
+
   ps_submit,
 
   ps_line,
   ps_text_metrics,
   ps_text_draw,
-
-  ps_chart_initialise,
-  ps_chart_finalise
 };
index 72edf17f08b7f6e9e0dec91ad8b56f5b9e87b88b..f8736e629c0148279ae8ed18b120c519d7de5271 100644 (file)
@@ -37,6 +37,7 @@
 
 #include <data/settings.h>
 
+#include "error.h"
 #include "minmax.h"
 #include "xalloc.h"
 
@@ -44,7 +45,6 @@
 #define _(msgid) gettext (msgid)
 \f
 const struct som_table_class tab_table_class;
-static char *command_name;
 
 /* Returns the font to use for a cell with the given OPTIONS. */
 static enum outp_font
@@ -57,13 +57,13 @@ options_to_font (unsigned options)
 
 /* Creates a table with NC columns and NR rows. */
 struct tab_table *
-tab_create (int nc, int nr, int reallocable UNUSED)
+tab_create (int nc, int nr)
 {
   struct tab_table *t;
 
   t = pool_create_container (struct tab_table, container);
+  t->ref_cnt = 1;
   t->col_style = TAB_COL_NONE;
-  t->col_group = 0;
   t->title = NULL;
   t->flags = SOMF_NONE;
   t->nr = nr;
@@ -81,21 +81,33 @@ tab_create (int nc, int nr, int reallocable UNUSED)
   memset (t->rv, UCHAR_MAX, nr * (nc + 1));
 
   t->dim = NULL;
-  t->w = t->h = NULL;
   t->col_ofs = t->row_ofs = 0;
 
   return t;
 }
 
-/* Destroys table T. */
+/* Increases T's reference count and, if this causes T's
+   reference count to reach 0, destroys T. */
 void
 tab_destroy (struct tab_table *t)
 {
-  assert (t != NULL);
+  assert (t->ref_cnt > 0);
+  if (--t->ref_cnt > 0)
+    return;
+  if (t->dim_free != NULL)
+    t->dim_free (t->dim_aux);
   free (t->title);
   pool_destroy (t->container);
 }
 
+/* Increases T's reference count. */
+void
+tab_ref (struct tab_table *t)
+{
+  assert (t->ref_cnt > 0);
+  t->ref_cnt++;
+}
+
 /* Sets the width and height of a table, in columns and rows,
    respectively.  Use only to reduce the size of a table, since it
    does not change the amount of allocated memory. */
@@ -110,7 +122,7 @@ tab_resize (struct tab_table *t, int nc, int nr)
     }
   if (nr != -1)
     {
-      assert (nr + t->row_ofs <= t->nr);
+      assert (nr + t->row_ofs <= tab_nr (t));
       t->nr = nr + t->row_ofs;
     }
 }
@@ -133,16 +145,16 @@ tab_realloc (struct tab_table *t, int nc, int nr)
     tab_offset (t, 0, 0);
 
   if (nc == -1)
-    nc = t->nc;
+    nc = tab_nc (t);
   if (nr == -1)
-    nr = t->nr;
+    nr = tab_nr (t);
 
-  assert (nc == t->nc);
+  assert (nc == tab_nc (t));
 
   if (nc > t->cf)
     {
-      int mr1 = MIN (nr, t->nr);
-      int mc1 = MIN (nc, t->nc);
+      int mr1 = MIN (nr, tab_nr (t));
+      int mc1 = MIN (nc, tab_nc (t));
 
       struct substring *new_cc;
       unsigned char *new_ct;
@@ -152,9 +164,9 @@ tab_realloc (struct tab_table *t, int nc, int nr)
       new_ct = pool_malloc (t->container, nr * nc);
       for (r = 0; r < mr1; r++)
        {
-         memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
-         memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
-         memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
+         memcpy (&new_cc[r * nc], &t->cc[r * tab_nc (t)], mc1 * sizeof *t->cc);
+         memcpy (&new_ct[r * nc], &t->ct[r * tab_nc (t)], mc1);
+         memset (&new_ct[r * nc + tab_nc (t)], TAB_EMPTY, nc - tab_nc (t));
        }
       pool_free (t->container, t->cc);
       pool_free (t->container, t->ct);
@@ -162,7 +174,7 @@ tab_realloc (struct tab_table *t, int nc, int nr)
       t->ct = new_ct;
       t->cf = nc;
     }
-  else if (nr != t->nr)
+  else if (nr != tab_nr (t))
     {
       t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
       t->ct = pool_realloc (t->container, t->ct, nr * nc);
@@ -170,15 +182,15 @@ tab_realloc (struct tab_table *t, int nc, int nr)
       t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
       t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
 
-      if (nr > t->nr)
+      if (nr > tab_nr (t))
        {
-         memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
-         memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
-                  (nr - t->nr) * (nc + 1));
+         memset (&t->rh[nc * (tab_nr (t) + 1)], TAL_0, (nr - tab_nr (t)) * nc);
+         memset (&t->rv[(nc + 1) * tab_nr (t)], UCHAR_MAX,
+                  (nr - tab_nr (t)) * (nc + 1));
        }
     }
 
-  memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
+  memset (&t->ct[nc * tab_nr (t)], TAB_EMPTY, nc * (nr - tab_nr (t)));
 
   t->nr = nr;
   t->nc = nc;
@@ -210,14 +222,12 @@ tab_headers (struct tab_table *table, int l, int r, int t, int b)
 /* Set up table T so that, when it is an appropriate size, it will be
    displayed across the page in columns.
 
-   STYLE is a TAB_COL_* constant.  GROUP is the number of rows to take
-   as a unit. */
+   STYLE is a TAB_COL_* constant. */
 void
-tab_columns (struct tab_table *t, int style, int group)
+tab_columns (struct tab_table *t, int style)
 {
   assert (t != NULL);
   t->col_style = style;
-  t->col_group = group;
 }
 \f
 /* Rules. */
@@ -230,16 +240,16 @@ tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
   assert (t != NULL);
 
 #if DEBUGGING
-  if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
-      || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
-      || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
+  if (x + t->col_ofs < 0 || x + t->col_ofs > tab_nc (t)
+      || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
+      || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
     {
       printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
                "table size (%d,%d)\n"),
              x, t->col_ofs, x + t->col_ofs,
              y1, t->row_ofs, y1 + t->row_ofs,
              y2, t->row_ofs, y2 + t->row_ofs,
-             t->nc, t->nr);
+             tab_nc (t), tab_nr (t));
       return;
     }
 #endif
@@ -249,10 +259,10 @@ tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
   y2 += t->row_ofs;
 
   assert (x  > 0);
-  assert (x  < t->nc);
+  assert (x  < tab_nc (t));
   assert (y1 >= 0);
   assert (y2 >= y1);
-  assert (y2 <=  t->nr);
+  assert (y2 <=  tab_nr (t));
 
   if (style != -1)
     {
@@ -274,10 +284,10 @@ tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
   y += t->row_ofs;
 
   assert (y >= 0);
-  assert (y <= t->nr);
+  assert (y <= tab_nr (t));
   assert (x2 >= x1 );
   assert (x1 >= 0 );
-  assert (x2 < t->nc);
+  assert (x2 < tab_nc (t));
 
   if (style != -1)
     {
@@ -300,10 +310,10 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
   assert (t != NULL);
 
 #if DEBUGGING
-  if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
-      || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
-      || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
-      || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
+  if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t)
+      || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t)
+      || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
+      || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
     {
       printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
                "in table size (%d,%d)\n"),
@@ -311,7 +321,7 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
              y1, t->row_ofs, y1 + t->row_ofs,
              x2, t->col_ofs, x2 + t->col_ofs,
              y2, t->row_ofs, y2 + t->row_ofs,
-             t->nc, t->nr);
+             tab_nc (t), tab_nr (t));
       NOT_REACHED ();
     }
 #endif
@@ -325,8 +335,8 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
   assert (y2 >= y1);
   assert (x1 >= 0);
   assert (y1 >= 0);
-  assert (x2 < t->nc);
-  assert (y2 < t->nr);
+  assert (x2 < tab_nc (t));
+  assert (y2 < tab_nr (t));
 
   if (f_h != -1)
     {
@@ -386,12 +396,22 @@ tab_title (struct tab_table *t, const char *title, ...)
   va_end (args);
 }
 
-/* Set DIM_FUNC as the dimension function for table T. */
+/* Set DIM_FUNC, which will be passed auxiliary data AUX, as the
+   dimension function for table T.
+
+   DIM_FUNC must not assume that it is called from the same
+   context as tab_dim; for example, table T might be kept in
+   memory and, thus, DIM_FUNC might be called after the currently
+   running command completes.  If it is non-null, FREE_FUNC is
+   called when the table is destroyed, to allow any data
+   allocated for use by DIM_FUNC to be freed.  */
 void
-tab_dim (struct tab_table *t, tab_dim_func *dim_func, void *aux)
+tab_dim (struct tab_table *t,
+         tab_dim_func *dim_func, tab_dim_free_func *free_func, void *aux)
 {
-  assert (t != NULL && t->dim == NULL);
+  assert (t->dim == NULL);
   t->dim = dim_func;
+  t->dim_free = free_func;
   t->dim_aux = aux;
 }
 
@@ -400,85 +420,76 @@ tab_dim (struct tab_table *t, tab_dim_func *dim_func, void *aux)
    wrapping.  The width will be no larger than the page width minus
    left and right rule widths. */
 int
-tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
+tab_natural_width (const struct tab_rendering *r, int col)
 {
-  int width;
+  const struct tab_table *t = r->table;
+  int width, row, max_width;
 
-  assert (t != NULL && c >= 0 && c < t->nc);
-  {
-    int r;
+  assert (col >= 0 && col < tab_nc (t));
 
-    for (width = r = 0; r < t->nr; r++)
-      {
-       struct outp_text text;
-       unsigned char opt = t->ct[c + r * t->cf];
-        int w;
+  width = 0;
+  for (row = 0; row < tab_nr (t); row++)
+    {
+      struct outp_text text;
+      unsigned char opt = t->ct[col + row * t->cf];
+      int w;
 
-       if (opt & (TAB_JOIN | TAB_EMPTY))
-         continue;
+      if (opt & (TAB_JOIN | TAB_EMPTY))
+        continue;
 
-       text.string = t->cc[c + r * t->cf];
-       text.justification = OUTP_LEFT;
-        text.font = options_to_font (opt);
-        text.h = text.v = INT_MAX;
+      text.string = t->cc[col + row * t->cf];
+      text.justification = OUTP_LEFT;
+      text.font = options_to_font (opt);
+      text.h = text.v = INT_MAX;
 
-       d->class->text_metrics (d, &text, &w, NULL);
-       if (w > width)
-         width = w;
-      }
-  }
+      r->driver->class->text_metrics (r->driver, &text, &w, NULL);
+      if (w > width)
+        width = w;
+    }
 
   if (width == 0)
     {
       /* FIXME: This is an ugly kluge to compensate for the fact
          that we don't let joined cells contribute to column
          widths. */
-      width = d->prop_em_width * 8;
+      width = r->driver->prop_em_width * 8;
     }
 
-  {
-    const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
-
-    if (width > clamp)
-      width = clamp;
-  }
-
-  return width;
+  max_width = r->driver->width - r->wrv[0] - r->wrv[tab_nc (t)];
+  return MIN (width, max_width);
 }
 
 /* Returns the natural height of row R in table T for driver D, that
    is, the minimum height necessary to display the information in the
    cell at the widths set for each column. */
 int
-tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
+tab_natural_height (const struct tab_rendering *r, int row)
 {
-  int height;
+  const struct tab_table *t = r->table;
+  int height, col;
 
-  assert (t != NULL && r >= 0 && r < t->nr);
+  assert (row >= 0 && row < tab_nr (t));
 
-  {
-    int c;
-
-    for (height = d->font_height, c = 0; c < t->nc; c++)
-      {
-       struct outp_text text;
-       unsigned char opt = t->ct[c + r * t->cf];
-        int h;
-
-       if (opt & (TAB_JOIN | TAB_EMPTY))
-         continue;
-
-       text.string = t->cc[c + r * t->cf];
-        text.justification = OUTP_LEFT;
-        text.font = options_to_font (opt);
-       text.h = t->w[c];
-        text.v = INT_MAX;
-       d->class->text_metrics (d, &text, NULL, &h);
-
-       if (h > height)
-         height = h;
-      }
-  }
+  height = r->driver->font_height;
+  for (col = 0; col < tab_nc (t); col++)
+    {
+      struct outp_text text;
+      unsigned char opt = t->ct[col + row * t->cf];
+      int h;
+
+      if (opt & (TAB_JOIN | TAB_EMPTY))
+        continue;
+
+      text.string = t->cc[col + row * t->cf];
+      text.justification = OUTP_LEFT;
+      text.font = options_to_font (opt);
+      text.h = r->w[col];
+      text.v = INT_MAX;
+      r->driver->class->text_metrics (r->driver, &text, NULL, &h);
+
+      if (h > height)
+        height = h;
+    }
 
   return height;
 }
@@ -486,18 +497,16 @@ tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
 /* Callback function to set all columns and rows to their natural
    dimensions.  Not really meant to be called directly.  */
 void
-tab_natural_dimensions (struct tab_table *t, struct outp_driver *d,
-                        void *aux UNUSED)
+tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
 {
+  const struct tab_table *t = r->table;
   int i;
 
-  assert (t != NULL);
-
-  for (i = 0; i < t->nc; i++)
-    t->w[i] = tab_natural_width (t, d, i);
+  for (i = 0; i < tab_nc (t); i++)
+    r->w[i] = tab_natural_width (r, i);
 
-  for (i = 0; i < t->nr; i++)
-    t->h[i] = tab_natural_height (t, d, i);
+  for (i = 0; i < tab_nr (t); i++)
+    r->h[i] = tab_natural_height (r, i);
 }
 
 \f
@@ -515,14 +524,14 @@ tab_value (struct tab_table *table, int c, int r, unsigned char opt,
   assert (table != NULL && v != NULL && f != NULL);
 #if DEBUGGING
   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
-      || c + table->col_ofs >= table->nc
-      || r + table->row_ofs >= table->nr)
+      || c + table->col_ofs >= tab_nc (table)
+      || r + table->row_ofs >= tab_nr (table))
     {
       printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
              "(%d,%d)\n",
              c, table->col_ofs, c + table->col_ofs,
              r, table->row_ofs, r + table->row_ofs,
-             table->nc, table->nr);
+             tab_nc (table), tab_nr (table));
       return;
     }
 #endif
@@ -547,22 +556,22 @@ tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
   assert (table != NULL && w <= 40);
 
   assert (c >= 0);
-  assert (c < table->nc);
+  assert (c < tab_nc (table));
   assert (r >= 0);
-  assert (r < table->nr);
+  assert (r < tab_nr (table));
 
   f = fmt_for_output (FMT_F, w, d);
 
 #if DEBUGGING
   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
-      || c + table->col_ofs >= table->nc
-      || r + table->row_ofs >= table->nr)
+      || c + table->col_ofs >= tab_nc (table)
+      || r + table->row_ofs >= tab_nr (table))
     {
       printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
              "(%d,%d)\n",
              c, table->col_ofs, c + table->col_ofs,
              r, table->row_ofs, r + table->row_ofs,
-             table->nc, table->nr);
+             tab_nc (table), tab_nr (table));
       return;
     }
 #endif
@@ -593,9 +602,9 @@ tab_double (struct tab_table *table, int c, int r, unsigned char opt,
   assert (table != NULL);
 
   assert (c >= 0);
-  assert (c < table->nc);
+  assert (c < tab_nc (table));
   assert (r >= 0);
-  assert (r < table->nr);
+  assert (r < tab_nr (table));
 
   if ( fmt == NULL)
     fmt = settings_get_format ();
@@ -604,14 +613,14 @@ tab_double (struct tab_table *table, int c, int r, unsigned char opt,
 
 #if DEBUGGING
   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
-      || c + table->col_ofs >= table->nc
-      || r + table->row_ofs >= table->nr)
+      || c + table->col_ofs >= tab_nc (table)
+      || r + table->row_ofs >= tab_nr (table))
     {
       printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
              "(%d,%d)\n",
              c, table->col_ofs, c + table->col_ofs,
              r, table->row_ofs, r + table->row_ofs,
-             table->nc, table->nr);
+             tab_nc (table), tab_nr (table));
       return;
     }
 #endif
@@ -629,21 +638,21 @@ tab_double (struct tab_table *table, int c, int r, unsigned char opt,
 static void
 do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
 {
-  assert (c >= 0);
-  assert (r >= 0);
-  assert (c < table->nc);
-  assert (r < table->nr);
+  assert (c >= 0 );
+  assert (r >= 0 );
+  assert (c < tab_nc (table));
+  assert (r < tab_nr (table));
 
 #if DEBUGGING
   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
-      || c + table->col_ofs >= table->nc
-      || r + table->row_ofs >= table->nr)
+      || c + table->col_ofs >= tab_nc (table)
+      || r + table->row_ofs >= tab_nr (table))
     {
       printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
              "(%d,%d)\n",
              c, table->col_ofs, c + table->col_ofs,
              r, table->row_ofs, r + table->row_ofs,
-             table->nc, table->nr);
+             tab_nc (table), tab_nr (table));
       return;
     }
 #endif
@@ -685,14 +694,14 @@ do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
   assert (y1 + table->row_ofs >= 0);
   assert (y2 >= y1);
   assert (x2 >= x1);
-  assert (y2 + table->row_ofs < table->nr);
-  assert (x2 + table->col_ofs < table->nc);
+  assert (y2 + table->row_ofs < tab_nr (table));
+  assert (x2 + table->col_ofs < tab_nc (table));
 
 #if DEBUGGING
-  if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
-      || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
-      || x2 < x1 || x2 + table->col_ofs >= table->nc
-      || y2 < y2 || y2 + table->row_ofs >= table->nr)
+  if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= tab_nc (table)
+      || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= tab_nr (table)
+      || x2 < x1 || x2 + table->col_ofs >= tab_nc (table)
+      || y2 < y2 || y2 + table->row_ofs >= tab_nr (table))
     {
       printf ("tab_joint_text(): bad cell "
              "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
@@ -700,7 +709,7 @@ do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
              y1, table->row_ofs, y1 + table->row_ofs,
              x2, table->col_ofs, x2 + table->col_ofs,
              y2, table->row_ofs, y2 + table->row_ofs,
-             table->nc, table->nr);
+             tab_nc (table), tab_nr (table));
       return;
     }
 #endif
@@ -708,7 +717,6 @@ do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
   tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
 
   j = pool_alloc (table->container, sizeof *j);
-  j->hit = 0;
   j->x1 = x1 + table->col_ofs;
   j->y1 = y1 + table->row_ofs;
   j->x2 = ++x2 + table->col_ofs;
@@ -764,50 +772,25 @@ tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, int y2,
                      pool_vasprintf (table->container, format, args));
   va_end (args);
 }
-
-/* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
-void
-tab_raw (struct tab_table *table, int c, int r, unsigned opt,
-        struct substring *string)
-{
-  assert (table != NULL && string != NULL);
-
-#if DEBUGGING
-  if (c + table->col_ofs < 0 || r + table->row_ofs < 0
-      || c + table->col_ofs >= table->nc
-      || r + table->row_ofs >= table->nr)
-    {
-      printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
-             "(%d,%d)\n",
-             c, table->col_ofs, c + table->col_ofs,
-             r, table->row_ofs, r + table->row_ofs,
-             table->nc, table->nr);
-      return;
-    }
-#endif
-
-  table->cc[c + r * table->cf] = *string;
-  table->ct[c + r * table->cf] = opt;
-}
 \f
 /* Miscellaneous. */
 
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-nowrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
 {
-  t->w[0] = tab_natural_width (t, d, 0);
-  t->h[0] = d->font_height;
+  r->w[0] = tab_natural_width (r, 0);
+  r->h[0] = r->driver->font_height;
 }
 
 /* Sets the widths of all the columns and heights of all the rows in
    table T for driver D. */
 static void
-wrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+wrap_dim (struct tab_rendering *r, void *aux UNUSED)
 {
-  t->w[0] = tab_natural_width (t, d, 0);
-  t->h[0] = tab_natural_height (t, d, 0);
+  r->w[0] = tab_natural_width (r, 0);
+  r->h[0] = tab_natural_height (r, 0);
 }
 
 static void
@@ -815,7 +798,7 @@ do_tab_output_text (struct tab_table *t, int options, char *text)
 {
   do_tab_text (t, 0, 0, options, text);
   tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
-  tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL);
+  tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL, NULL);
   tab_submit (t);
 }
 
@@ -825,7 +808,7 @@ do_tab_output_text (struct tab_table *t, int options, char *text)
 void
 tab_output_text (int options, const char *text)
 {
-  struct tab_table *table = tab_create (1, 1, 0);
+  struct tab_table *table = tab_create (1, 1);
   do_tab_output_text (table, options, pool_strdup (table->container, text));
 }
 
@@ -839,7 +822,7 @@ tab_output_text_format (int options, const char *format, ...)
   struct tab_table *table;
   va_list args;
 
-  table = tab_create (1, 1, 0);
+  table = tab_create (1, 1);
 
   va_start (args, format);
   do_tab_output_text (table, options,
@@ -880,14 +863,14 @@ tab_offset (struct tab_table *t, int col, int row)
 
   assert (t != NULL);
 #if DEBUGGING
-  if (row < -1 || row > t->nr)
+  if (row < -1 || row > tab_nr (t))
     {
-      printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
+      printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
       NOT_REACHED ();
     }
-  if (col < -1 || col > t->nc)
+  if (col < -1 || col > tab_nc (t))
     {
-      printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
+      printf ("tab_offset(): col=%d in %d-column table\n", col, tab_nc (t));
       NOT_REACHED ();
     }
 #endif
@@ -909,29 +892,46 @@ tab_next_row (struct tab_table *t)
   assert (t != NULL);
   t->cc += t->cf;
   t->ct += t->cf;
-  if (++t->row_ofs >= t->nr)
-    tab_realloc (t, -1, t->nr * 4 / 3);
+  if (++t->row_ofs >= tab_nr (t))
+    tab_realloc (t, -1, tab_nr (t) * 4 / 3);
 }
 \f
-static struct tab_table *t;
-static struct outp_driver *d;
-static int tab_hit;
+/* Return the number of columns and rows in the table into N_COLUMNS
+   and N_ROWS, respectively. */
+static void
+tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
+{
+  struct tab_table *t = t_->ext;
+  *n_columns = t->nc;
+  *n_rows = t->nr;
+}
 
-/* Set the current table to TABLE. */
+/* Return the column style for this table into STYLE. */
 static void
-tabi_table (struct som_entity *table)
+tabi_columns (struct som_entity *t_, int *style)
 {
-  assert (table != NULL);
-  assert (table->type == SOM_TABLE);
+  struct tab_table *t = t_->ext;
+  *style = t->col_style;
+}
 
-  t = table->ext;
-  tab_offset (t, 0, 0);
+/* Return the number of header rows/columns on the left, right, top,
+   and bottom sides into HL, HR, HT, and HB, respectively. */
+static void
+tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
+{
+  struct tab_table *t = t_->ext;
+  *hl = t->l;
+  *hr = t->r;
+  *ht = t->t;
+  *hb = t->b;
+}
 
-  assert (t->w == NULL && t->h == NULL);
-  t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
-  t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
-  t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
-  t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
+/* Return flags set for the current table into FLAGS. */
+static void
+tabi_flags (struct som_entity *t_, unsigned *flags)
+{
+  struct tab_table *t = t_->ext;
+  *flags = t->flags;
 }
 
 /* Returns the line style to use for spacing purposes for a rule
@@ -953,153 +953,127 @@ rule_to_spacing_type (unsigned char type)
     }
 }
 
-/* Set the current output device to DRIVER. */
-static void
-tabi_driver (struct outp_driver *driver)
+static void *
+tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
+                  int hl, int hr, int ht, int hb)
 {
-  int c, r;
+  const struct tab_table *t = t_->ext;
+  struct tab_rendering *r;
+  int col, row;
   int i;
 
-  assert (driver != NULL);
-  d = driver;
+  tab_offset (t_->ext, 0, 0);
+
+  r = xmalloc (sizeof *r);
+  r->table = t;
+  r->driver = driver;
+  r->w = xnmalloc (tab_nc (t), sizeof *r->w);
+  r->h = xnmalloc (tab_nr (t), sizeof *r->h);
+  r->hrh = xnmalloc (tab_nr (t) + 1, sizeof *r->hrh);
+  r->wrv = xnmalloc (tab_nc (t) + 1, sizeof *r->wrv);
+  r->l = hl;
+  r->r = hr;
+  r->t = ht;
+  r->b = hb;
 
   /* Figure out sizes of rules. */
-  for (r = 0; r <= t->nr; r++)
+  for (row = 0; row <= tab_nr (t); row++)
     {
       int width = 0;
-      for (c = 0; c < t->nc; c++)
+      for (col = 0; col < tab_nc (t); col++)
         {
-          unsigned char rh = t->rh[c + r * t->cf];
+          unsigned char rh = t->rh[col + row * t->cf];
           int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
           if (w > width)
             width = w;
         }
-      t->hrh[r] = width;
+      r->hrh[row] = width;
     }
 
-  for (c = 0; c <= t->nc; c++)
+  for (col = 0; col <= tab_nc (t); col++)
     {
       int width = 0;
-      for (r = 0; r < t->nr; r++)
+      for (row = 0; row < tab_nr (t); row++)
         {
-          unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
+          unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
           int w;
           if (*rv == UCHAR_MAX)
-            *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
+            *rv = col != 0 && col != tab_nc (t) ? TAL_GAP : TAL_0;
           w = driver->vert_line_width[rule_to_spacing_type (*rv)];
           if (w > width)
             width = w;
         }
-      t->wrv[c] = width;
+      r->wrv[col] = width;
     }
 
-#if DEBUGGING
-  for (i = 0; i < t->nr; i++)
-    t->h[i] = -1;
-  for (i = 0; i < t->nc; i++)
-    t->w[i] = -1;
-#endif
-
-  assert (t->dim != NULL);
-  t->dim (t, d, t->dim_aux);
+  /* Determine row heights and columns widths. */
+  for (i = 0; i < tab_nr (t); i++)
+    r->h[i] = -1;
+  for (i = 0; i < tab_nc (t); i++)
+    r->w[i] = -1;
 
-#if DEBUGGING
-  {
-    int error = 0;
-
-    for (i = 0; i < t->nr; i++)
-      {
-       if (t->h[i] == -1)
-         {
-           printf ("Table row %d height not initialized.\n", i);
-           error = 1;
-         }
-       assert (t->h[i] > 0);
-      }
+  t->dim (r, t->dim_aux);
 
-    for (i = 0; i < t->nc; i++)
-      {
-       if (t->w[i] == -1)
-         {
-           printf ("Table column %d width not initialized.\n", i);
-           error = 1;
-         }
-       assert (t->w[i] > 0);
-      }
-  }
-#endif
+  for (i = 0; i < tab_nr (t); i++)
+    if (r->h[i] < 0)
+      error (0, 0, "height of table row %d is %d (not initialized?)",
+             i, r->h[i]);
+  for (i = 0; i < tab_nc (t); i++)
+    if (r->w[i] < 0)
+      error (0, 0, "width of table column %d is %d (not initialized?)",
+             i, r->w[i]);
 
   /* Add up header sizes. */
-  for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
-    t->wl += t->w[i] + t->wrv[i + 1];
-  for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
-    t->ht += t->h[i] + t->hrh[i + 1];
-  for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
-    t->wr += t->w[i] + t->wrv[i + 1];
-  for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
-    t->hb += t->h[i] + t->hrh[i + 1];
+  for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
+    r->wl += r->w[i] + r->wrv[i + 1];
+  for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
+    r->ht += r->h[i] + r->hrh[i + 1];
+  for (i = tab_nc (t) - r->r, r->wr = r->wrv[i]; i < tab_nc (t); i++)
+    r->wr += r->w[i] + r->wrv[i + 1];
+  for (i = tab_nr (t) - r->b, r->hb = r->hrh[i]; i < tab_nr (t); i++)
+    r->hb += r->h[i] + r->hrh[i + 1];
 
   /* Title. */
   if (!(t->flags & SOMF_NO_TITLE))
-    t->ht += d->font_height;
+    r->ht += driver->font_height;
+
+  return r;
 }
 
-/* Return the number of columns and rows in the table into N_COLUMNS
-   and N_ROWS, respectively. */
 static void
-tabi_count (int *n_columns, int *n_rows)
+tabi_render_free (void *r_)
 {
-  assert (n_columns != NULL && n_rows != NULL);
-  *n_columns = t->nc;
-  *n_rows = t->nr;
-}
+  struct tab_rendering *r = r_;
 
-static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
+  free (r->w);
+  free (r->h);
+  free (r->hrh);
+  free (r->wrv);
+  free (r);
+}
 
 /* Return the horizontal and vertical size of the entire table,
    including headers, for the current output device, into HORIZ and
    VERT. */
 static void
-tabi_area (int *horiz, int *vert)
+tabi_area (void *r_, int *horiz, int *vert)
 {
-  assert (horiz != NULL && vert != NULL);
-
-  {
-    int w, c;
-
-    for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
-        c < t->nc - t->r; c++)
-      w += t->w[c] + t->wrv[c];
-    *horiz = w;
-  }
-
-  {
-    int h, r;
-    for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
-        r < t->nr - t->b; r++)
-      h += t->h[r] + t->hrh[r];
-    *vert = h;
-  }
-}
-
-/* Return the column style for this table into STYLE. */
-static void
-tabi_columns (int *style)
-{
-  assert (style != NULL);
-  *style = t->col_style;
-}
-
-/* Return the number of header rows/columns on the left, right, top,
-   and bottom sides into HL, HR, HT, and HB, respectively. */
-static void
-tabi_headers (int *hl, int *hr, int *ht, int *hb)
-{
-  assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
-  *hl = t->l;
-  *hr = t->r;
-  *ht = t->t;
-  *hb = t->b;
+  struct tab_rendering *r = r_;
+  const struct tab_table *t = r->table;
+  int width, col;
+  int height, row;
+
+  width = 0;
+  for (col = r->l + 1, width = r->wl + r->wr + r->w[tab_l (t)];
+       col < tab_nc (t) - r->r; col++)
+    width += r->w[col] + r->wrv[col];
+  *horiz = width;
+
+  height = 0;
+  for (row = r->t + 1, height = r->ht + r->hb + r->h[tab_t (t)];
+       row < tab_nr (t) - tab_b (t); row++)
+    height += r->h[row] + r->hrh[row];
+  *vert = height;
 }
 
 /* Determines the number of rows or columns (including appropriate
@@ -1110,32 +1084,35 @@ tabi_headers (int *hl, int *hr, int *ht, int *hb)
    space the selected rows/columns (including appropriate headers)
    filled. */
 static void
-tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
+tabi_cumulate (void *r_, int cumtype, int start, int *end,
+               int max, int *actual)
 {
-  int n;
-  int *d;
-  int *r;
+  const struct tab_rendering *r = r_;
+  const struct tab_table *t = r->table;
+  int limit;
+  int *cells, *rules;
   int total;
+  int idx;
 
   assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
   if (cumtype == SOM_ROWS)
     {
-      assert (start >= 0 && start < t->nr);
-      n = t->nr - t->b;
-      d = &t->h[start];
-      r = &t->hrh[start + 1];
-      total = t->ht + t->hb;
+      assert (start >= 0 && start < tab_nr (t));
+      limit = tab_nr (t) - r->b;
+      cells = &r->h[start];
+      rules = &r->hrh[start + 1];
+      total = r->ht + r->hb;
     }
   else
     {
-      assert (start >= 0 && start < t->nc);
-      n = t->nc - t->r;
-      d = &t->w[start];
-      r = &t->wrv[start + 1];
-      total = t->wl + t->wr;
+      assert (start >= 0 && start < tab_nc (t));
+      limit = tab_nc (t) - tab_r (t);
+      cells = &r->w[start];
+      rules = &r->wrv[start + 1];
+      total = r->wl + r->wr;
     }
 
-  total += *d++;
+  total += *cells++;
   if (total > max)
     {
       if (end)
@@ -1145,180 +1122,139 @@ tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
       return;
     }
 
-  {
-    int x;
-
-    for (x = start + 1; x < n; x++)
-      {
-       int amt = *d++ + *r++;
-
-       total += amt;
-       if (total > max)
-         {
-           total -= amt;
-           break;
-         }
-      }
-
-    if (end)
-      *end = x;
-
-    if (actual)
-      *actual = total;
-  }
-}
-
-/* Return flags set for the current table into FLAGS. */
-static void
-tabi_flags (unsigned *flags)
-{
-  assert (flags != NULL);
-  *flags = t->flags;
-}
-
-/* Returns true if the table will fit in the given page WIDTH,
-   false otherwise. */
-static bool
-tabi_fits_width (int width)
-{
-  int i;
-
-  for (i = t->l; i < t->nc - t->r; i++)
-    if (t->wl + t->wr + t->w[i] > width)
-      return false;
-
-  return true;
-}
+  for (idx = start + 1; idx < limit; idx++)
+    {
+      int amt = *cells++ + *rules++;
 
-/* Returns true if the table will fit in the given page LENGTH,
-   false otherwise. */
-static bool
-tabi_fits_length (int length)
-{
-  int i;
+      total += amt;
+      if (total > max)
+        {
+          total -= amt;
+          break;
+        }
+    }
 
-  for (i = t->t; i < t->nr - t->b; i++)
-    if (t->ht + t->hb + t->h[i] > length)
-      return false;
+  if (end)
+    *end = idx;
 
-  return true;
-}
-
-/* Sets the number of header rows/columns on the left, right, top,
-   and bottom sides to HL, HR, HT, and HB, respectively. */
-static void
-tabi_set_headers (int hl, int hr, int ht, int hb)
-{
-  t->l = hl;
-  t->r = hr;
-  t->t = ht;
-  t->b = hb;
+  if (actual)
+    *actual = total;
 }
 
 /* Render title for current table, with major index X and minor index
    Y.  Y may be zero, or X and Y may be zero, but X should be nonzero
    if Y is nonzero. */
 static void
-tabi_title (int x, int y)
+tabi_title (void *r_, int x, int y, int table_num, int subtable_num,
+            const char *command_name)
 {
-  char buf[1024];
-  char *cp;
+  const struct tab_rendering *r = r_;
+  const struct tab_table *t = r->table;
+  struct outp_text text;
+  struct string title;
 
   if (t->flags & SOMF_NO_TITLE)
     return;
 
-  cp = spprintf (buf, "%d.%d", table_num, subtable_num);
+  ds_init_empty (&title);
+  ds_put_format (&title,"%d.%d", table_num, subtable_num);
   if (x && y)
-    cp = spprintf (cp, "(%d:%d)", x, y);
+    ds_put_format (&title, "(%d:%d)", x, y);
   else if (x)
-    cp = spprintf (cp, "(%d)", x);
+    ds_put_format (&title, "(%d)", x);
   if (command_name != NULL)
-    cp = spprintf (cp, " %s", command_name);
-  cp = stpcpy (cp, ".  ");
+    ds_put_format (&title, " %s", command_name);
+  ds_put_cstr (&title, ".  ");
   if (t->title != NULL)
-    {
-      size_t length = strlen (t->title);
-      memcpy (cp, t->title, length);
-      cp += length;
-    }
-  *cp = 0;
-
-  {
-    struct outp_text text;
-
-    text.font = OUTP_PROPORTIONAL;
-    text.justification = OUTP_LEFT;
-    text.string = ss_buffer (buf, cp - buf);
-    text.h = d->width;
-    text.v = d->font_height;
-    text.x = 0;
-    text.y = d->cp_y;
-    d->class->text_draw (d, &text);
-  }
+    ds_put_cstr (&title, t->title);
+
+  text.font = OUTP_PROPORTIONAL;
+  text.justification = OUTP_LEFT;
+  text.string = ds_ss (&title);
+  text.h = r->driver->width;
+  text.v = r->driver->font_height;
+  text.x = 0;
+  text.y = r->driver->cp_y;
+  r->driver->class->text_draw (r->driver, &text);
+
+  ds_destroy (&title);
 }
 
-static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
+static int render_strip (const struct tab_rendering *,
+                         int x, int y, int r, int c1, int c2, int r1, int r2);
 
-/* Renders columns C0...C1, plus headers, of rows R0...R1,
-   at the given vertical position Y.
-   C0 and C1 count vertical rules as columns,
-   but R0 and R1 do not count horizontal rules as rows.
-   Returns the vertical position after rendering. */
-static int
-render_rows (int y, int c0, int c1, int r0, int r1)
+static void
+add_range (int ranges[][2], int *np, int start, int end)
 {
-  int r;
-  for (r = r0; r < r1; r++)
+  int n = *np;
+  if (n == 0 || start > ranges[n - 1][1])
     {
-      int x = d->cp_x;
-      x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
-      x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
-      x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
-      y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
+      ranges[n][0] = start;
+      ranges[n][1] = end;
+      ++*np;
     }
-  return y;
+  else
+    ranges[n - 1][1] = end;
 }
 
 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
    current position on the current output device.  */
 static void
-tabi_render (int c0, int r0, int c1, int r1)
+tabi_render (void *r_, int c0, int r0, int c1, int r1)
 {
-  int y;
+  const struct tab_rendering *r = r_;
+  const struct tab_table *t = r->table;
+  int rows[3][2], cols[3][2];
+  int n_row_ranges, n_col_ranges;
+  int y, i;
+
+  /* Rows to render, counting horizontal rules as rows.  */
+  n_row_ranges = 0;
+  add_range (rows, &n_row_ranges, 0, tab_t (t) * 2 + 1);
+  add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
+  add_range (rows, &n_row_ranges, (tab_nr (t) - tab_b (t)) * 2,
+             tab_nr (t) * 2 + 1);
+
+  /* Columns to render, counting vertical rules as columns. */
+  n_col_ranges = 0;
+  add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
+  add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
+  add_range (cols, &n_col_ranges, (tab_nc (t) - r->r) * 2, tab_nc (t) * 2 + 1);
+
+  y = r->driver->cp_y;
+  if (!(t->flags & SOMF_NO_TITLE))
+    y += r->driver->font_height;
+  for (i = 0; i < n_row_ranges; i++)
+    {
+      int row;
 
-  tab_hit++;
+      for (row = rows[i][0]; row < rows[i][1]; row++)
+        {
+          int x, j;
 
-  y = d->cp_y;
-  if (!(t->flags & SOMF_NO_TITLE))
-    y += d->font_height;
+          x = r->driver->cp_x;
+          for (j = 0; j < n_col_ranges; j++)
+            x = render_strip (r, x, y, row,
+                              cols[j][0], cols[j][1],
+                              rows[i][0], rows[i][1]);
 
-  y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
-  y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
-  y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
+          y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
+        }
+    }
 }
 
 const struct som_table_class tab_table_class =
   {
-    tabi_table,
-    tabi_driver,
-
     tabi_count,
-    tabi_area,
-    NULL,
-    NULL,
     tabi_columns,
-    NULL,
     tabi_headers,
-    NULL,
-    tabi_cumulate,
     tabi_flags,
-    tabi_fits_width,
-    tabi_fits_length,
 
-    NULL,
-    NULL,
-    tabi_set_headers,
+    tabi_render_init,
+    tabi_render_free,
 
+    tabi_area,
+    tabi_cumulate,
     tabi_title,
     tabi_render,
   };
@@ -1360,56 +1296,63 @@ rule_to_draw_type (unsigned char type)
 
 /* Returns the horizontal rule at the given column and row. */
 static int
-get_hrule (int c, int r)
+get_hrule (const struct tab_table *t, int col, int row)
 {
-  return t->rh[c + r * t->cf];
+  return t->rh[col + row * t->cf];
 }
 
 /* Returns the vertical rule at the given column and row. */
 static int
-get_vrule (int c, int r)
+get_vrule (const struct tab_table *t, int col, int row)
 {
-  return t->rv[c + r * (t->cf + 1)];
+  return t->rv[col + row * (t->cf + 1)];
 }
 
 /* Renders the horizontal rule at the given column and row
    at (X,Y) on the page. */
 static void
-render_horz_rule (int x, int y, int c, int r)
+render_horz_rule (const struct tab_rendering *r,
+                  int x, int y, int col, int row)
 {
-  enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
+  enum outp_line_style style;
+  style = rule_to_draw_type (get_hrule (r->table, col, row));
   if (style != OUTP_L_NONE)
-    d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
-                    OUTP_L_NONE, style, OUTP_L_NONE, style);
+    r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
+                            OUTP_L_NONE, style, OUTP_L_NONE, style);
 }
 
 /* Renders the vertical rule at the given column and row
    at (X,Y) on the page. */
 static void
-render_vert_rule (int x, int y, int c, int r)
+render_vert_rule (const struct tab_rendering *r,
+                  int x, int y, int col, int row)
 {
-  enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
+  enum outp_line_style style;
+  style = rule_to_draw_type (get_vrule (r->table, col, row));
   if (style != OUTP_L_NONE)
-    d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
-                    style, OUTP_L_NONE, style, OUTP_L_NONE);
+    r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
+                            style, OUTP_L_NONE, style, OUTP_L_NONE);
 }
 
 /* Renders the rule intersection at the given column and row
    at (X,Y) on the page. */
 static void
-render_rule_intersection (int x, int y, int c, int r)
+render_rule_intersection (const struct tab_rendering *r,
+                          int x, int y, int col, int row)
 {
+  const struct tab_table *t = r->table;
+
   /* Bounds of intersection. */
   int x0 = x;
   int y0 = y;
-  int x1 = x + t->wrv[c];
-  int y1 = y + t->hrh[r];
+  int x1 = x + r->wrv[col];
+  int y1 = y + r->hrh[row];
 
   /* Lines on each side of intersection. */
-  int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
-  int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
-  int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
-  int right = c < t->nc ? get_hrule (c, r) : TAL_0;
+  int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
+  int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
+  int bottom = row < tab_nr (t) ? get_vrule (t, col, row) : TAL_0;
+  int right = col < tab_nc (t) ? get_hrule (t, col, row) : TAL_0;
 
   /* Output style for each line. */
   enum outp_line_style o_top = rule_to_draw_type (top);
@@ -1419,36 +1362,37 @@ render_rule_intersection (int x, int y, int c, int r)
 
   if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
       || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
-    d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
+    r->driver->class->line (r->driver, x0, y0, x1, y1,
+                            o_top, o_left, o_bottom, o_right);
 }
 
 /* Returns the width of columns C1...C2 exclusive,
    including interior but not exterior rules. */
 static int
-strip_width (int c1, int c2)
+strip_width (const struct tab_rendering *r, int c1, int c2)
 {
   int width = 0;
   int c;
 
   for (c = c1; c < c2; c++)
-    width += t->w[c] + t->wrv[c + 1];
+    width += r->w[c] + r->wrv[c + 1];
   if (c1 < c2)
-    width -= t->wrv[c2];
+    width -= r->wrv[c2];
   return width;
 }
 
 /* Returns the height of rows R1...R2 exclusive,
    including interior but not exterior rules. */
 static int
-strip_height (int r1, int r2)
+strip_height (const struct tab_rendering *r, int r1, int r2)
 {
   int height = 0;
-  int r;
+  int row;
 
-  for (r = r1; r < r2; r++)
-    height += t->h[r] + t->hrh[r + 1];
+  for (row = r1; row < r2; row++)
+    height += r->h[row] + r->hrh[row + 1];
   if (r1 < r2)
-    height -= t->hrh[r2];
+    height -= r->hrh[r2];
   return height;
 }
 
@@ -1456,9 +1400,11 @@ strip_height (int r1, int r2)
    page.  Also renders joined cells that extend as far to the
    right as C1 and as far down as R1. */
 static void
-render_cell (int x, int y, int c, int r, int c1, int r1)
+render_cell (const struct tab_rendering *r,
+             int x, int y, int col, int row, int c1, int r1)
 {
-  const int index = c + (r * t->cf);
+  const struct tab_table *t = r->table;
+  const int index = col + (row * t->cf);
   unsigned char type = t->ct[index];
   struct substring *content = &t->cc[index];
 
@@ -1470,11 +1416,11 @@ render_cell (int x, int y, int c, int r, int c1, int r1)
           text.font = options_to_font (type);
           text.justification = translate_justification (type);
           text.string = *content;
-          text.h = t->w[c];
-          text.v = t->h[r];
+          text.h = r->w[col];
+          text.v = r->h[row];
           text.x = x;
           text.y = y;
-          d->class->text_draw (d, &text);
+          r->driver->class->text_draw (r->driver, &text);
         }
     }
   else
@@ -1482,63 +1428,50 @@ render_cell (int x, int y, int c, int r, int c1, int r1)
       struct tab_joined_cell *j
         = (struct tab_joined_cell *) ss_data (*content);
 
-      if (j->hit != tab_hit)
+      if (j->x1 == col && j->y1 == row)
         {
-          j->hit = tab_hit;
-
-          if (j->x1 == c && j->y1 == r)
-            {
-              struct outp_text text;
-              text.font = options_to_font (type);
-              text.justification = translate_justification (type);
-              text.string = j->contents;
-              text.x = x;
-              text.y = y;
-              text.h = strip_width (j->x1, MIN (j->x2, c1));
-              text.v = strip_height (j->y1, MIN (j->y2, r1));
-              d->class->text_draw (d, &text);
-            }
+          struct outp_text text;
+          text.font = options_to_font (type);
+          text.justification = translate_justification (type);
+          text.string = j->contents;
+          text.x = x;
+          text.y = y;
+          text.h = strip_width (r, j->x1, MIN (j->x2, c1));
+          text.v = strip_height (r, j->y1, MIN (j->y2, r1));
+          r->driver->class->text_draw (r->driver, &text);
         }
     }
 }
 
 /* Render contiguous strip consisting of columns C0...C1, exclusive,
-   on row R, at (X,Y).  Returns X position after rendering.
+   on row ROW, at (X,Y).  Returns X position after rendering.
    Also renders joined cells that extend beyond that strip,
    cropping them to lie within rendering region (C0,R0)-(C1,R1).
    C0 and C1 count vertical rules as columns.
-   R counts horizontal rules as rows, but R0 and R1 do not. */
+   ROW counts horizontal rules as rows, but R0 and R1 do not. */
 static int
-render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
+render_strip (const struct tab_rendering *r,
+              int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
 {
-  int c;
+  int col;
 
-  for (c = c0; c < c1; c++)
-    if (c & 1)
+  for (col = c0; col < c1; col++)
+    if (col & 1)
       {
-        if (r & 1)
-          render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
+        if (row & 1)
+          render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
         else
-          render_horz_rule (x, y, c / 2, r / 2);
-        x += t->w[c / 2];
+          render_horz_rule (r, x, y, col / 2, row / 2);
+        x += r->w[col / 2];
       }
     else
       {
-        if (r & 1)
-          render_vert_rule (x, y, c / 2, r / 2);
+        if (row & 1)
+          render_vert_rule (r, x, y, col / 2, row / 2);
         else
-          render_rule_intersection (x, y, c / 2, r / 2);
-        x += t->wrv[c / 2];
+          render_rule_intersection (r, x, y, col / 2, row / 2);
+        x += r->wrv[col / 2];
       }
 
   return x;
 }
-
-/* Sets COMMAND_NAME as the name of the current command,
-   for embedding in output. */
-void
-tab_set_command_name (const char *command_name_)
-{
-  free (command_name);
-  command_name = command_name_ ? xstrdup (command_name_) : NULL;
-}
index 1748c24c855e71ec8c0212dac76d00662db76f67..f4fbc786f0440ea39da04ebfa0c54070ccec7cd1 100644 (file)
@@ -61,23 +61,24 @@ struct tab_joined_cell
   {
     int x1, y1;
     int x2, y2;
-    int hit;
     struct substring contents;
   };
 
 struct outp_driver;
 struct tab_table;
-typedef void tab_dim_func (struct tab_table *, struct outp_driver *,
-                           void *aux);
+struct tab_rendering;
+
+typedef void tab_dim_func (struct tab_rendering *, void *aux);
+typedef void tab_dim_free_func (void *aux);
 
 /* A table. */
 struct tab_table
   {
     struct pool *container;
+    int ref_cnt;                /* Reference count. */
 
     /* Contents. */
     int col_style;             /* Columns: One of TAB_COL_*. */
-    int col_group;             /* Number of rows per column group. */
     char *title;                /* Table title. */
     unsigned flags;            /* SOMF_*. */
     int nc, nr;                        /* Number of columns, rows. */
@@ -87,45 +88,51 @@ struct tab_table
     unsigned char *ct;         /* Cell types; unsigned char[nr][nc]. */
     unsigned char *rh;         /* Horiz rules; unsigned char[nr+1][nc]. */
     unsigned char *rv;         /* Vert rules; unsigned char[nr][nc+1]. */
+
+    /* Calculating row and column dimensions. */
     tab_dim_func *dim;         /* Calculates cell widths and heights. */
+    tab_dim_free_func *dim_free; /* Frees space allocated for dim function. */
     void *dim_aux;              /* Auxiliary data for dim function. */
 
-    /* Calculated during output. */
-    int *w;                    /* Column widths; [nc]. */
-    int *h;                    /* Row heights; [nr]. */
-    int *hrh;                  /* Heights of horizontal rules; [nr+1]. */
-    int *wrv;                  /* Widths of vertical rules; [nc+1]. */
-    int wl, wr, ht, hb;                /* Width/height of header rows/columns. */
-
     /* Editing info. */
     int col_ofs, row_ofs;      /* X and Y offsets. */
   };
 
-/* Number of rows in TABLE. */
-#define tab_nr(TABLE) ((TABLE)->nr)
-
-/* Number of columns in TABLE. */
-#define tab_nc(TABLE) ((TABLE)->nc)
+/* Number of rows or columns in TABLE. */
+static inline int tab_nr (const struct tab_table *table) { return table->nr; }
+static inline int tab_nc (const struct tab_table *table) { return table->nc; }
 
-/* Number of left header columns in TABLE. */
-#define tab_l(TABLE) ((TABLE)->l)
+/* Number of left/right/top/bottom header columns/rows in TABLE. */
+static inline int tab_l (const struct tab_table *table) { return table->l; }
+static inline int tab_r (const struct tab_table *table) { return table->r; }
+static inline int tab_t (const struct tab_table *table) { return table->t; }
+static inline int tab_b (const struct tab_table *table) { return table->b; }
 
-/* Number of right header columns in TABLE. */
-#define tab_r(TABLE) ((TABLE)->r)
+struct tab_rendering
+  {
+    const struct tab_table *table;
+    struct outp_driver *driver;
 
-/* Number of top header rows in TABLE. */
-#define tab_t(TABLE) ((TABLE)->t)
+    int *w;                    /* Column widths; [nc]. */
+    int *h;                    /* Row heights; [nr]. */
+    int *hrh;                  /* Heights of horizontal rules; [nr+1]. */
+    int *wrv;                  /* Widths of vertical rules; [nc+1]. */
 
-/* Number of bottom header rows in TABLE. */
-#define tab_b(TABLE) ((TABLE)->b)
+    /* These fields would be redundant with those in struct tab_table, except
+       that a table will be rendered with fewer header rows or columns than
+       requested when we are pressed for space. */
+    int l, r, t, b;            /* Number of header rows/columns. */
+    int wl, wr, ht, hb;                /* Width/height of header rows/columns. */
+  };
 
 /* Tables. */
-struct tab_table *tab_create (int nc, int nr, int reallocable);
+struct tab_table *tab_create (int nc, int nr);
 void tab_destroy (struct tab_table *);
+void tab_ref (struct tab_table *);
 void tab_resize (struct tab_table *, int nc, int nr);
 void tab_realloc (struct tab_table *, int nc, int nr);
 void tab_headers (struct tab_table *, int l, int r, int t, int b);
-void tab_columns (struct tab_table *, int style, int group);
+void tab_columns (struct tab_table *, int style);
 void tab_title (struct tab_table *, const char *, ...)
      PRINTF_FORMAT (2, 3);
 void tab_flags (struct tab_table *, unsigned);
@@ -133,9 +140,10 @@ void tab_submit (struct tab_table *);
 
 /* Dimensioning. */
 tab_dim_func tab_natural_dimensions;
-int tab_natural_width (struct tab_table *t, struct outp_driver *d, int c);
-int tab_natural_height (struct tab_table *t, struct outp_driver *d, int r);
-void tab_dim (struct tab_table *, tab_dim_func *, void *aux);
+int tab_natural_width (const struct tab_rendering *, int c);
+int tab_natural_height (const struct tab_rendering *, int r);
+void tab_dim (struct tab_table *,
+              tab_dim_func *, tab_dim_free_func *, void *aux);
 
 /* Rules. */
 void tab_hline (struct tab_table *, int style, int x1, int x2, int y);
@@ -176,11 +184,6 @@ void tab_joint_text_format (struct tab_table *, int x1, int y1, int x2, int y2,
                             unsigned opt, const char *, ...)
      PRINTF_FORMAT (7, 8);
 
-/* Cell low-level access. */
-#define tab_alloc(TABLE, AMT) pool_alloc ((TABLE)->container, (AMT))
-void tab_raw (struct tab_table *, int c, int r, unsigned opt,
-             struct substring *);
-
 /* Editing. */
 void tab_offset (struct tab_table *, int col, int row);
 void tab_next_row (struct tab_table *);
@@ -194,8 +197,5 @@ void tab_output_text (int options, const char *string);
 void tab_output_text_format (int options, const char *, ...)
      PRINTF_FORMAT (2, 3);
 
-/* Embedding the command name in the output. */
-void tab_set_command_name (const char *);
-
 #endif /* tab_h */
 
index c21b681d1f289f980e8bbcd274c2743726c73275..743d0b6dd77a85472de65b19967d79241df2cc5f 100644 (file)
@@ -1,7 +1,7 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
 include $(top_srcdir)/src/ui/terminal/automake.mk
-if WITHGUI
+if HAVE_GUI
 include $(top_srcdir)/src/ui/gui/automake.mk
 endif
 
index 7c06b8e47bf33c5811f4ea486d7e27454499f47d..05f362194cc207442d902d3b63fcd6d18adeb4f6 100644 (file)
@@ -26,6 +26,7 @@ src_ui_gui_psppire_LDADD = \
        src/libpspp.la \
        src/libpspp-core.la \
        $(GTK_LIBS) \
+       $(CAIRO_LIBS) \
        $(LIBINTL)
 
 src_ui_gui_psppiredir = $(pkgdatadir)
index 9336f7ce3d4dbe318fb8ed5fbdd5a890e3be57ed..aa2fae51ce127b95cfe6c607da50c2da95fe406f 100644 (file)
@@ -104,7 +104,5 @@ execute_syntax (struct getl_interface *sss)
 
   som_flush ();
 
-  psppire_output_window_reload ();
-
   return retval;
 }
index 11ec0603d4dfd98ee65954b1fed34af49feb4bc2..0c16bfa8140c0a78f4036c2c2ef2e3eeadb5023a 100644 (file)
@@ -34,6 +34,7 @@ which match particular strings */
 #include <ctype.h>
 #include <sys/types.h>
 #include <regex.h>
+#include <libpspp/cast.h>
 #include <libpspp/message.h>
 
 #include <gtk/gtk.h>
@@ -570,7 +571,8 @@ regexp_label_compare (const struct comparator *cmptr,
 static void
 regexp_destroy (struct comparator *cmptr)
 {
-  struct regexp_comparator *rec = (struct regexp_comparator *) cmptr;
+  struct regexp_comparator *rec
+    = UP_CAST (cmptr, struct regexp_comparator, parent);
 
   regfree (&rec->re);
 }
@@ -578,7 +580,8 @@ regexp_destroy (struct comparator *cmptr)
 static void
 cmptr_value_destroy (struct comparator *cmptr)
 {
-  struct value_comparator *vc = (struct value_comparator *) cmptr;
+  struct value_comparator *vc
+    = UP_CAST (cmptr, struct value_comparator, parent);
   value_destroy (&vc->pattern, var_get_width (cmptr->var));
 }
 
@@ -587,7 +590,7 @@ static struct comparator *
 value_comparator_create (const struct variable *var, const PsppireDict *dict, const char *target)
 {
   struct value_comparator *vc = xzalloc (sizeof (*vc));
-  struct comparator *cmptr = (struct comparator *) vc;
+  struct comparator *cmptr = &vc->parent;
 
   cmptr->flags = 0;
   cmptr->var = var;
@@ -606,7 +609,7 @@ string_comparator_create (const struct variable *var, const PsppireDict *dict,
                          enum string_cmp_flags flags)
 {
   struct string_comparator *ssc = xzalloc (sizeof (*ssc));
-  struct comparator *cmptr = (struct comparator *) ssc;
+  struct comparator *cmptr = &ssc->parent;
 
   cmptr->flags = flags;
   cmptr->var = var;
@@ -629,7 +632,7 @@ regexp_comparator_create (const struct variable *var, const PsppireDict *dict, c
 {
   int code;
   struct regexp_comparator *rec = xzalloc (sizeof (*rec));
-  struct comparator *cmptr = (struct comparator *) rec;
+  struct comparator *cmptr = &rec->parent;
 
   cmptr->flags = flags;
   cmptr->var = var;
index 0b709ee4cadee9fc91a8cb53bdc5a05324612169..50d92d46671a07444c4555891f5af8e0db78889a 100644 (file)
@@ -1,7 +1,7 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.2.0 on Sat Aug 11 17:19:54 2007 by john@marilyn-->
+<?xml version="1.0"?>
 <glade-interface>
+  <!-- interface-requires gtk+ 2.16 -->
+  <!-- interface-naming-policy toplevel-contextual -->
   <widget class="GtkWindow" id="output-viewer-window">
     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
     <property name="default_width">600</property>
@@ -16,7 +16,6 @@
             <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
             <child>
               <widget class="GtkMenuItem" id="menuitem1">
-                <property name="visible">False</property>
                 <property name="sensitive">False</property>
                 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                 <property name="label" translatable="yes">_File</property>
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                     <child>
                       <widget class="GtkImageMenuItem" id="file_save">
+                        <property name="label">gtk-save</property>
                         <property name="visible">True</property>
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="label" translatable="yes">gtk-save</property>
                         <property name="use_underline">True</property>
                         <property name="use_stock">True</property>
                       </widget>
                     </child>
                     <child>
                       <widget class="GtkImageMenuItem" id="file_save-as">
+                        <property name="label">gtk-save-as</property>
                         <property name="visible">True</property>
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="label" translatable="yes">gtk-save-as</property>
                         <property name="use_underline">True</property>
                         <property name="use_stock">True</property>
                       </widget>
@@ -49,7 +48,6 @@
             </child>
             <child>
               <widget class="GtkMenuItem" id="menuitem2">
-                <property name="visible">False</property>
                 <property name="sensitive">False</property>
                 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                 <property name="label" translatable="yes">_Edit</property>
@@ -60,9 +58,9 @@
                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
                     <child>
                       <widget class="GtkImageMenuItem" id="imagemenuitem7">
+                        <property name="label">gtk-copy</property>
                         <property name="visible">True</property>
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="label" translatable="yes">gtk-copy</property>
                         <property name="use_underline">True</property>
                         <property name="use_stock">True</property>
                       </widget>
           </widget>
           <packing>
             <property name="expand">False</property>
+            <property name="position">0</property>
           </packing>
         </child>
         <child>
-          <widget class="GtkScrolledWindow" id="scrolledwindow1">
+          <widget class="GtkHPaned" id="hpaned1">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-            <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+            <property name="position">112</property>
+            <property name="position_set">True</property>
             <child>
-              <widget class="GtkTextView" id="output-viewer-textview">
+              <widget class="GtkScrolledWindow" id="scrolledwindow2">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hscrollbar_policy">automatic</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <child>
+                  <widget class="GtkTreeView" id="overview">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="headers_visible">False</property>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="resize">False</property>
+                <property name="shrink">True</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkScrolledWindow" id="scrolledwindow1">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                <property name="editable">False</property>
-                <property name="left_margin">5</property>
+                <property name="hscrollbar_policy">automatic</property>
+                <property name="vscrollbar_policy">automatic</property>
+                <child>
+                  <widget class="GtkLayout" id="output">
+                    <property name="visible">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                  </widget>
+                </child>
               </widget>
+              <packing>
+                <property name="resize">True</property>
+                <property name="shrink">True</property>
+              </packing>
             </child>
           </widget>
           <packing>
index b0eff950c7e5e6b466b70edda79e9faf40702a8b..35216d4e518502d809fd8e02965d82945fd53f86 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2008  Free Software Foundation
+   Copyright (C) 2008, 2009  Free Software Foundation
 
    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
 #include "helper.h"
 
 #include <libpspp/message.h>
+#include <output/cairo.h>
+#include <output/manager.h>
+#include <output/output.h>
+#include <output/table.h>
 #include <stdlib.h>
 
 #include "about.h"
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
-
+enum
+  {
+    COL_TITLE,                  /* Table title. */
+    COL_Y,                      /* Y position of top of title. */
+    N_COLS
+  };
 
 static void psppire_output_window_base_finalize (PsppireOutputWindowClass *, gpointer);
 static void psppire_output_window_base_init     (PsppireOutputWindowClass *class);
@@ -106,13 +115,152 @@ psppire_output_window_base_finalize (PsppireOutputWindowClass *class,
                                     gpointer class_data)
 {
 }
-
-
 \f
+/* Output driver class. */
 
 static PsppireOutputWindow *the_output_viewer = NULL;
 
+static gboolean
+expose_event_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
+{
+  struct som_entity *entity = g_object_get_data (G_OBJECT (widget), "entity");
+  GdkWindow *window = widget->window;
+  cairo_t *cairo = gdk_cairo_create (GDK_DRAWABLE (window));
+  struct outp_driver *driver = xr_create_driver (cairo); /* XXX can fail */
+  struct tab_table *t = entity->ext;
+  void *rendering;
+
+  rendering = entity->class->render_init (entity, driver, tab_l (t),
+                                          tab_r (t), tab_t (t), tab_b (t));
+
+  entity->class->title (rendering, 0, 0,
+                        entity->table_num, entity->subtable_num,
+                        entity->command_name);
+  entity->class->render (rendering, tab_l (t), tab_t (t),
+                         tab_nc (t) - tab_r (t),
+                         tab_nr (t) - tab_b (t));
+
+  entity->class->render_free (rendering);
+  driver->class->close_driver (driver);
+  outp_free_driver (driver);
+  return TRUE;
+}
 
+static void
+psppire_output_submit (struct outp_driver *this, struct som_entity *entity)
+{
+  if (the_output_viewer == NULL)
+    {
+      the_output_viewer = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
+      gtk_widget_show_all (GTK_WIDGET (the_output_viewer));
+    }
+
+  if (entity->type == SOM_TABLE)
+    {
+      GdkWindow *window = GTK_WIDGET (the_output_viewer)->window;
+      cairo_t *cairo = gdk_cairo_create (GDK_DRAWABLE (window));
+      struct outp_driver *driver = xr_create_driver (cairo); /* XXX can fail */
+      struct tab_table *t = entity->ext;
+      GtkTreeStore *store;
+      GtkTreeIter item;
+      GtkTreePath *path;
+      GtkWidget *drawing_area;
+      void *rendering;
+      struct string title;
+      int tw, th;
+
+      tab_ref (t);
+      rendering = entity->class->render_init (entity, driver, tab_l (t),
+                                              tab_r (t), tab_t (t), tab_b (t));
+      entity->class->area (rendering, &tw, &th);
+
+      drawing_area = gtk_drawing_area_new ();
+      gtk_widget_modify_bg (GTK_WIDGET (drawing_area), GTK_STATE_NORMAL,
+                            &gtk_widget_get_style (drawing_area)->base[GTK_STATE_NORMAL]);
+      g_object_set_data (G_OBJECT (drawing_area),
+                         "entity", som_entity_clone (entity));
+      gtk_widget_set_size_request (drawing_area, tw / 1024, th / 1024);
+      gtk_layout_put (the_output_viewer->output, drawing_area,
+                      0, the_output_viewer->y);
+      gtk_widget_show (drawing_area);
+      g_signal_connect (G_OBJECT (drawing_area), "expose_event",
+                        G_CALLBACK (expose_event_callback), NULL);
+
+      entity->class->render_free (rendering);
+      driver->class->close_driver (driver);
+      outp_free_driver (driver);
+
+      store = GTK_TREE_STORE (gtk_tree_view_get_model (
+                                the_output_viewer->overview));
+
+      ds_init_empty (&title);
+      if (entity->table_num != the_output_viewer->last_table_num)
+        {
+          gtk_tree_store_append (store, &item, NULL);
+
+          ds_put_format (&title, "%d %s",
+                         entity->table_num, entity->command_name);
+          gtk_tree_store_set (store, &item,
+                              COL_TITLE, ds_cstr (&title),
+                              COL_Y, the_output_viewer->y,
+                              -1);
+
+          /* XXX shouldn't save a GtkTreeIter */
+          the_output_viewer->last_table_num = entity->table_num;
+          the_output_viewer->last_top_level = item;
+        }
+
+      gtk_tree_store_append (store, &item,
+                             &the_output_viewer->last_top_level);
+      ds_clear (&title);
+      ds_put_format (&title, "%d.%d %s",
+                     entity->table_num, entity->subtable_num,
+                     t->title ? t->title : entity->command_name);
+      gtk_tree_store_set (store, &item,
+                          COL_TITLE, ds_cstr (&title),
+                          COL_Y, the_output_viewer->y,
+                          -1);
+      ds_destroy (&title);
+
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (store),
+                                      &the_output_viewer->last_top_level);
+      gtk_tree_view_expand_row (the_output_viewer->overview, path, TRUE);
+      gtk_tree_path_free (path);
+
+      if (tw / 1024 > the_output_viewer->max_width)
+        the_output_viewer->max_width = tw / 1024;
+      the_output_viewer->y += th / 1024;
+
+      gtk_layout_set_size (the_output_viewer->output,
+                           the_output_viewer->max_width, the_output_viewer->y);
+    }
+
+  gtk_window_set_urgency_hint (GTK_WINDOW (the_output_viewer), TRUE);
+}
+
+static struct outp_class psppire_output_class =
+  {
+    "PSPPIRE",                  /* name */
+    true,                       /* special */
+    NULL,                       /* open_driver */
+    NULL,                       /* close_driver */
+    NULL,                       /* open_page */
+    NULL,                       /* close_page */
+    NULL,                       /* flush */
+    NULL,                       /* output_chart */
+    psppire_output_submit,      /* submit */
+    NULL,                       /* line */
+    NULL,                       /* text_metrics */
+    NULL,                       /* text_draw */
+  };
+
+void
+psppire_output_window_setup (void)
+{
+  outp_register_driver (outp_allocate_driver (&psppire_output_class,
+                                              "PSPPIRE", 0));
+}
+\f
 int viewer_length = 16;
 int viewer_width = 59;
 
@@ -127,8 +275,6 @@ on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
 
   the_output_viewer = NULL;
 
-  unlink (output_file_name());
-
   return FALSE;
 }
 
@@ -139,99 +285,78 @@ cancel_urgency (GtkWindow *window,  gpointer data)
 {
   gtk_window_set_urgency_hint (window, FALSE);
 }
-/* Sets width and length according to the new size
-   of the output window */
+
 static void
-on_textview_resize (GtkWidget     *widget,
-                   GtkAllocation *allocation,
-                   gpointer       user_data)
+on_row_activate (GtkTreeView *overview,
+                 GtkTreePath *path,
+                 GtkTreeViewColumn *column,
+                 PsppireOutputWindow *window)
 {
-  PangoContext * context ;
-  PangoLayout *layout ;
-  PangoRectangle logical;
-  GtkStyle *style;
-  gint right_margin, left_margin;
-  GtkTextView *text_view = GTK_TEXT_VIEW (widget);
-
-  context = gtk_widget_create_pango_context (widget);
-  layout = pango_layout_new (context);
-
-  style = gtk_widget_get_style (widget);
-
-  pango_layout_set_font_description (layout, style->font_desc);
-
-  /* Find the width of one character.  We can use any character, because
-     the textview has a monospaced font */
-  pango_layout_set_text (layout, "M", 1);
-
-  pango_layout_get_extents (layout,  NULL, &logical);
-
-  left_margin = gtk_text_view_get_left_margin (text_view);
-  right_margin = gtk_text_view_get_right_margin (text_view);
-
-  viewer_length = allocation->height / PANGO_PIXELS (logical.height);
-  viewer_width = (allocation->width - right_margin - left_margin)
-    / PANGO_PIXELS (logical.width);
-
-  g_object_unref (G_OBJECT (layout));
-  g_object_unref (G_OBJECT (context));
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  GtkAdjustment *vadj;
+  GValue value = {0};
+  double y, min, max;
+
+  model = gtk_tree_view_get_model (overview);
+  if (!gtk_tree_model_get_iter (model, &iter, path))
+    return;
+
+  gtk_tree_model_get_value (model, &iter, COL_Y, &value);
+  y = g_value_get_long (&value);
+  g_value_unset (&value);
+
+  vadj = gtk_layout_get_vadjustment (window->output);
+  min = vadj->lower;
+  max = vadj->upper - vadj->page_size;
+  if (y < min)
+    y = min;
+  else if (y > max)
+    y = max;
+  gtk_adjustment_set_value (vadj, y);
 }
 
-
 static void
 psppire_output_window_init (PsppireOutputWindow *window)
 {
-  GtkBuilder *xml = builder_new ("output-viewer.ui");
-
-  GtkWidget *box = gtk_vbox_new (FALSE, 0);
-
-  GtkWidget *sw = get_widget_assert (xml, "scrolledwindow1");
-
-  GtkWidget *menubar = get_widget_assert (xml, "menubar1");
-
-  window->textview = get_widget_assert (xml, "output-viewer-textview");
-
+  GtkTreeViewColumn *column;
+  GtkCellRenderer *renderer;
+  GtkBuilder *xml;
 
-  gtk_container_add (GTK_CONTAINER (window), box);
+  xml = builder_new ("output-viewer.ui");
 
+  gtk_widget_reparent (get_widget_assert (xml, "vbox1"), GTK_WIDGET (window));
 
-  g_object_ref (menubar);
-  gtk_widget_unparent (menubar);
+  window->output = GTK_LAYOUT (get_widget_assert (xml, "output"));
+  window->y = 0;
 
-  g_object_ref (sw);
-  gtk_widget_unparent (sw);
+  window->overview = GTK_TREE_VIEW (get_widget_assert (xml, "overview"));
+  gtk_tree_view_set_model (window->overview,
+                           GTK_TREE_MODEL (gtk_tree_store_new (
+                                             N_COLS,
+                                             G_TYPE_STRING, /* COL_TITLE */
+                                             G_TYPE_LONG))); /* COL_Y */
+  window->last_table_num = -1;
 
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_append_column (GTK_TREE_VIEW (window->overview), column);
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, renderer, TRUE);
+  gtk_tree_view_column_add_attribute (column, renderer, "text", COL_TITLE);
 
-  gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
-  gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0);
+  g_signal_connect (GTK_TREE_VIEW (window->overview),
+                    "row-activated", G_CALLBACK (on_row_activate), window);
 
-
-  gtk_widget_show_all (box);
+  gtk_widget_modify_bg (GTK_WIDGET (window->output), GTK_STATE_NORMAL,
+                        &gtk_widget_get_style (GTK_WIDGET (window->output))->base[GTK_STATE_NORMAL]);
 
   connect_help (xml);
 
-  window->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->textview));
-
   g_signal_connect (window,
                    "focus-in-event",
                    G_CALLBACK (cancel_urgency),
                    NULL);
 
-  {
-    /* Output uses ascii characters for tabular material.
-       So we need a monospaced font otherwise it'll look silly */
-    PangoFontDescription *font_desc =
-      pango_font_description_from_string ("monospace");
-
-    gtk_widget_modify_font (window->textview, font_desc);
-    pango_font_description_free (font_desc);
-  }
-
-  g_signal_connect (window->textview, "size-allocate",
-                   G_CALLBACK (on_textview_resize), NULL);
-
-  window->fp = NULL;
-
   g_signal_connect (get_action_assert (xml,"help_about"),
                    "activate",
                    G_CALLBACK (about_new),
@@ -269,108 +394,3 @@ psppire_output_window_new (void)
                                   "description", _("Output Viewer"),
                                   NULL));
 }
-
-static void reload_viewer (PsppireOutputWindow *ow);
-
-void
-psppire_output_window_reload (void)
-{
-  struct stat buf;
-
-  /* If there is no output, then don't do anything */
-  if (0 != stat (output_file_name(), &buf))
-    return ;
-
-  if ( NULL == the_output_viewer )
-    {
-      the_output_viewer = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
-      gtk_widget_show (GTK_WIDGET (the_output_viewer));
-    }
-
-  reload_viewer (the_output_viewer);
-
-}
-
-
-static void
-reload_viewer (PsppireOutputWindow *ow)
-{
-  GtkTextIter end_iter;
-  GtkTextMark *mark ;
-
-  char *line = NULL;
-
-  gboolean chars_inserted = FALSE;
-
-  gtk_text_buffer_get_end_iter (ow->buffer, &end_iter);
-
-  line = xrealloc (line, sizeof (char) * (viewer_width + 1));
-
-  mark = gtk_text_buffer_create_mark (ow->buffer, NULL, &end_iter, TRUE);
-
-#ifdef __CYGWIN__
-  /*
-    Apparently Windoze is not capabale of writing to a file whilst
-    another (or the same) process is reading from it.   Therefore, we
-    must close the file after reading it, and clear the entire buffer
-    before writing to it.
-    This will be slower for large buffers, but should work
-    (in so far as anything ever works on windows).
-  */
-  {
-    GtkTextIter start_iter;
-    FILE *fp = fopen (output_file_name(), "r");
-    if ( !fp)
-      {
-       g_critical ("Cannot open %s\n", output_file_name());
-       return;
-      }
-
-    /* Delete all the entire buffer */
-    gtk_text_buffer_get_start_iter (ow->buffer, &start_iter);
-    gtk_text_buffer_delete (ow->buffer, &start_iter, &end_iter);
-
-
-    gtk_text_buffer_get_start_iter (ow->buffer, &start_iter);
-    /* Read in the next lot of text */
-    while (fgets (line, viewer_width + 1, fp) != NULL)
-      {
-       chars_inserted = TRUE;
-       gtk_text_buffer_insert (ow->buffer, &start_iter, line, -1);
-      }
-
-    fclose (fp);
-  }
-#else
-  {
-    if ( ow->fp == NULL)
-      {
-       ow->fp = fopen (output_file_name(), "r");
-       if ( ow->fp == NULL)
-         {
-           g_critical ("Cannot open %s\n", output_file_name());
-           return;
-         }
-      }
-
-    /* Read in the next lot of text */
-    while (fgets (line, viewer_width + 1, ow->fp) != NULL)
-      {
-       chars_inserted = TRUE;
-       gtk_text_buffer_insert (ow->buffer, &end_iter, line, -1);
-      }
-  }
-#endif
-
-  /* Scroll to where the start of this lot of text begins */
-  gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (ow->textview),
-                               mark,
-                               0.1, TRUE, 0.0, 0.0);
-
-
-  if ( chars_inserted )
-    gtk_window_set_urgency_hint ( GTK_WINDOW (ow), TRUE);
-}
-
-
-
index d11eca618d84c9c18c86e5dc7bd184b6f9771a75..606a950475fc6ecc1d4833fb7eea46e9faf8ccdf 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2008  Free Software Foundation
+   Copyright (C) 2008, 2009  Free Software Foundation
 
    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
@@ -31,10 +31,6 @@ extern int viewer_length;
 extern int viewer_width ;
 
 
-#define OUTPUT_FILE_NAME "psppire.txt"
-
-
-
 G_BEGIN_DECLS
 
 #define PSPPIRE_OUTPUT_WINDOW_TYPE            (psppire_output_window_get_type ())
@@ -56,9 +52,13 @@ struct _PsppireOutputWindow
   PsppireWindow parent;
 
   /* <private> */
-  GtkTextBuffer *buffer;  /* The buffer which contains the text */
-  GtkWidget *textview ;
-  FILE *fp;               /* The file it's viewing */
+  GtkLayout *output;
+  int max_width;
+  int y;
+
+  GtkTreeView *overview;
+  int last_table_num;
+  GtkTreeIter last_top_level;
 };
 
 struct _PsppireOutputWindowClass
@@ -70,9 +70,7 @@ struct _PsppireOutputWindowClass
 GType      psppire_output_window_get_type        (void);
 GtkWidget* psppire_output_window_new             (void);
 
-
-void psppire_output_window_reload (void);
-
+void psppire_output_window_setup (void);
 
 G_END_DECLS
 
index 1cd7ee803c736a740b373a53b035c7dcef6bd90a..961998d9266a8ab4da61fd4f102109606c46cc6d 100644 (file)
@@ -120,25 +120,7 @@ initialize (struct command_line_processor *clp, int argc, char **argv)
 
   create_icon_factory ();
 
-  {
-    const char *filename = output_file_name ();
-
-    struct string config_string;
-
-    ds_init_empty (&config_string);
-
-    ds_put_format (&config_string,
-                  "gui:ascii:screen:squeeze=on headers=off top-margin=0 "
-                  "bottom-margin=0 paginate=off length=auto width=auto "
-                  "emphasis=none "
-                  "output-file=\"%s\" append=yes", filename);
-
-    outp_configure_driver_line (ds_ss (&config_string));
-
-    unlink (filename);
-
-    ds_destroy (&config_string);
-  }
+  psppire_output_window_setup ();
 
   journal_enable ();
   textdomain (PACKAGE);
@@ -338,16 +320,3 @@ parse_non_options (int key, char *arg, struct argp_state *state)
 
 
 const struct argp non_option_argp = {NULL, parse_non_options, 0, 0, 0, 0, 0};
-
-
-const char *
-output_file_name (void)
-{
-  const char *dir = default_output_path ();
-  static char *filename = NULL;
-
-  if ( NULL == filename )
-    filename = xasprintf ("%s%s", dir, OUTPUT_FILE_NAME);
-
-  return filename;
-}
index 214370513b7369087344f60c636f450704f3b7a7..6ec866c93a11d7997f2de2fd66583b040e5b06b3 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2006  Free Software Foundation
+   Copyright (C) 2006, 2009  Free Software Foundation
 
    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
@@ -19,6 +19,7 @@
 
 #include <libpspp/getl.h>
 #include <libpspp/compiler.h>
+#include <libpspp/cast.h>
 #include <libpspp/str.h>
 
 #include <stdlib.h>
@@ -72,7 +73,8 @@ read_line_from_buffer (struct getl_interface *i,
   gchar *text;
   GtkTextIter next_line;
 
-  struct syntax_editor_source *ses = (struct syntax_editor_source *) i;
+  struct syntax_editor_source *ses
+    = UP_CAST (i, struct syntax_editor_source, parent);
 
   if ( gtk_text_iter_compare (&ses->i, &ses->end) >= 0)
     return false;
@@ -124,5 +126,5 @@ create_syntax_editor_source (GtkTextBuffer *buffer,
   ses->parent.location = location;
 
 
-  return (struct getl_interface *) ses;
+  return &ses->parent;
 }
index 14eacabfadddb5842e81f444855233b0e903ec03..5f43ff498f10a6c4910a53bec9fc5bfda82a87fb 100644 (file)
@@ -324,7 +324,7 @@ on_remove (GtkWidget *w, gpointer data)
   struct val_labs_dialog *dialog = data;
 
   union value value;
-  const struct val_lab *vl;
+  struct val_lab *vl;
 
   get_selected_tuple (dialog, &value, NULL);
   vl = val_labs_lookup (dialog->labs, &value);
index 9cde11c9110641e1c6f39fa1aa4de959865cb74b..81b896dc878839d44590f9e3205ebc53d5e07fb0 100644 (file)
@@ -25,6 +25,7 @@ src_ui_terminal_pspp_LDADD = \
        src/ui/libuicommon.la \
        src/libpspp.la \
        src/libpspp-core.la \
+       $(CAIRO_LIBS) \
        $(NCURSES_LIBS) \
        $(LIBICONV) \
        $(LIBINTL) $(LIBREADLINE)
index 7b23f38d2f359b58ecec1635970904d732edad5c..f85e4e76b17a2aef2e9ca0c8aac27d8296341fcd 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+   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
@@ -31,6 +31,7 @@
 #include <data/file-name.h>
 #include <data/settings.h>
 #include <language/command.h>
+#include <libpspp/cast.h>
 #include <libpspp/message.h>
 #include <libpspp/str.h>
 #include <libpspp/version.h>
@@ -107,8 +108,7 @@ static bool
 read_interactive (struct getl_interface *s,
                   struct string *line)
 {
-  struct readln_source *is  =
-    (struct readln_source *) s ;
+  struct readln_source *is  = UP_CAST (s, struct readln_source, parent);
 
   return is->interactive_func (line, prompt_get_style ());
 }
@@ -215,7 +215,7 @@ create_readln_source (void)
   rlns->parent.read = read_interactive;
   rlns->parent.close = readln_close;
 
-  return (struct getl_interface *) rlns;
+  return &rlns->parent;
 }
 
 
index e8cbffa5e6334408bbd69dc4c7c2a7e9dbb72829..8d6e68316834d707254b5888f92d1b41b1064bb6 100644 (file)
@@ -212,7 +212,7 @@ check_pattern (const struct range_set *rs, unsigned int pattern)
      caching. */
   for (start = 0; start <= 32; start++)
     {
-      struct range_set *nonconst_rs = (struct range_set *) rs;
+      struct range_set *nonconst_rs = CONST_CAST (struct range_set *, rs);
       nonconst_rs->cache_end = 0;
       s1 = range_set_scan (rs, start);
       s2 = next_1bit (pattern, start);