From: Ben Pfaff Date: Tue, 11 Aug 2009 21:18:26 +0000 (-0700) Subject: Merge master into output branch. X-Git-Tag: sid-i386-build98~5 X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cb72db62c20ecab427229110820c5b053d0663c4;hp=c2f0df181038fe9975d642096e65ea48ca491acd;p=pspp-builds.git Merge master into output branch. --- diff --git a/INSTALL b/INSTALL index d3a469cf..686109e4 100644 --- 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 df10031e..7f6c2a0e 100644 --- a/NEWS +++ b/NEWS @@ -1,10 +1,17 @@ PSPP NEWS -- history of user-visible changes. -Time-stamp: <2009-05-24 22:25:04 blp> +Time-stamp: <2009-07-29 20:52:41 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 5af73108..b5bd839e 100644 --- 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. diff --git a/acinclude.m4 b/acinclude.m4 index 75fcb0d0..6141edd2 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -1,4 +1,4 @@ -dnl Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. +dnl Copyright (C) 2005, 2006, 2007, 2009 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. @@ -36,40 +36,6 @@ AC_DEFUN([PSPP_PERL], fi ]) -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. diff --git a/config/devices b/config/devices index 48513fe6..3bc685a0 100644 --- a/config/devices +++ b/config/devices @@ -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: diff --git a/configure.ac b/configure.ac index c44fdd56..5ed6d74f 100644 --- a/configure.ac +++ b/configure.ac @@ -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 @@ -36,27 +36,39 @@ AM_GNU_GETTEXT_VERSION([0.17]) 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.22 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.22 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 diff --git a/doc/configuring.texi b/doc/configuring.texi index 164d9ab1..a1007d92 100644 --- a/doc/configuring.texi +++ b/doc/configuring.texi @@ -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 diff --git a/lib/automake.mk b/lib/automake.mk index 6b20c674..9bde4afc 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -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 diff --git a/src/data/caseproto.c b/src/data/caseproto.c index 1a40213a..9837013a 100644 --- a/src/data/caseproto.c +++ b/src/data/caseproto.c @@ -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); diff --git a/src/data/caseproto.h b/src/data/caseproto.h index b85a9f32..b0f45c41 100644 --- a/src/data/caseproto.h +++ b/src/data/caseproto.h @@ -22,6 +22,7 @@ #include #include #include +#include #include /* 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; } diff --git a/src/data/casereader.c b/src/data/casereader.c index a1550ac5..5ae5a0a8 100644 --- a/src/data/casereader.c +++ b/src/data/casereader.c @@ -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; diff --git a/src/data/casewindow.c b/src/data/casewindow.c index 9b04b941..936b6aa9 100644 --- a/src/data/casewindow.c +++ b/src/data/casewindow.c @@ -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)) diff --git a/src/data/datasheet.c b/src/data/datasheet.c index fa24d8ce..4abc526f 100644 --- a/src/data/datasheet.c +++ b/src/data/datasheet.c @@ -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 diff --git a/src/data/dictionary.c b/src/data/dictionary.c index 67af049b..7379d194 100644 --- a/src/data/dictionary.c +++ b/src/data/dictionary.c @@ -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. */ diff --git a/src/data/procedure.c b/src/data/procedure.c index b762214d..5e013a14 100644 --- a/src/data/procedure.c +++ b/src/data/procedure.c @@ -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)); } } diff --git a/src/data/subcase.c b/src/data/subcase.c index 6ffaa4c2..03098685 100644 --- a/src/data/subcase.c +++ b/src/data/subcase.c @@ -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) { diff --git a/src/data/value-labels.c b/src/data/value-labels.c index c8061f7b..34223955 100644 --- a/src/data/value-labels.c +++ b/src/data/value-labels.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -169,7 +170,8 @@ 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 = CONST_CAST (struct val_lab *, + val_labs_lookup (vls, value)); if (vl != NULL) { atom_destroy (vl->label); @@ -183,7 +185,7 @@ val_labs_replace (struct val_labs *vls, const union value *value, void val_labs_remove (struct val_labs *vls, const struct val_lab *label_) { - struct val_lab *label = (struct val_lab *) label_; + struct val_lab *label = CONST_CAST (struct val_lab *, label_); hmap_delete (&vls->labels, &label->node); value_destroy (&label->value, vls->width); atom_destroy (label->label); diff --git a/src/data/variable.c b/src/data/variable.c index d1e30864..05edc57e 100644 --- a/src/data/variable.c +++ b/src/data/variable.c @@ -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. */ diff --git a/src/language/automake.mk b/src/language/automake.mk index 4210b7fc..3052b523 100644 --- a/src/language/automake.mk +++ b/src/language/automake.mk @@ -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 \ diff --git a/src/language/command.c b/src/language/command.c index c00d94b9..74f99a89 100644 --- a/src/language/command.c +++ b/src/language/command.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #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)); diff --git a/src/language/control/repeat.c b/src/language/control/repeat.c index 32847bb4..cb608f76 100644 --- a/src/language/control/repeat.c +++ b/src/language/control/repeat.c @@ -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 #include #include +#include #include #include #include @@ -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); } diff --git a/src/language/data-io/data-parser.c b/src/language/data-io/data-parser.c index 020f8e4c..fe78aeb3 100644 --- a/src/language/data-io/data-parser.c +++ b/src/language/data-io/data-parser.c @@ -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++) { diff --git a/src/language/data-io/list.q b/src/language/data-io/list.q index c3f9b088..614fa127 100644 --- a/src/language/data-io/list.q +++ b/src/language/data-io/list.q @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include #include @@ -63,8 +63,10 @@ /* (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 ("\n", x->file); - } - } - else - NOT_REACHED (); + fputs ("\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 (" \n", x->file); + fputs (" \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 (" ", x->file); - html_put_cell_contents (d, TAB_FIX, ss_buffer (s, print->w)); - free (s); - fputs ("\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 (" ", x->file); + html_put_cell_contents (d, TAB_FIX, ss_cstr (s)); + fputs ("\n", x->file); + + free (s); + } - fputs (" \n", x->file); - } - else - NOT_REACHED (); + fputs (" \n", x->file); + } + else + NOT_REACHED (); + } } + /* Local Variables: mode: c diff --git a/src/language/data-io/print.c b/src/language/data-io/print.c index e345477b..dd1e26d2 100644 --- a/src/language/data-io/print.c +++ b/src/language/data-io/print.c @@ -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) { diff --git a/src/language/dictionary/split-file.c b/src/language/dictionary/split-file.c index 5d2b42d7..d2f59ccc 100644 --- a/src/language/dictionary/split-file.c +++ b/src/language/dictionary/split-file.c @@ -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); diff --git a/src/language/dictionary/sys-file-info.c b/src/language/dictionary/sys-file-info.c index 6a0c81ac..1202045d 100644 --- a/src/language/dictionary/sys-file-info.c +++ b/src/language/dictionary/sys-file-info.c @@ -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); } @@ -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); diff --git a/src/language/lexer/variable-parser.c b/src/language/lexer/variable-parser.c index 1194110e..8a39dbf0 100644 --- a/src/language/lexer/variable-parser.c +++ b/src/language/lexer/variable-parser.c @@ -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 diff --git a/src/language/stats/aggregate.c b/src/language/stats/aggregate.c index 08d2f5e1..891e0cca 100644 --- a/src/language/stats/aggregate.c +++ b/src/language/stats/aggregate.c @@ -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: diff --git a/src/language/stats/binomial.c b/src/language/stats/binomial.c index 26e0257c..bc87a94d 100644 --- a/src/language/stats/binomial.c +++ b/src/language/stats/binomial.c @@ -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) { diff --git a/src/language/stats/chisquare.c b/src/language/stats/chisquare.c index 4593df41..bc1b6474 100644 --- a/src/language/stats/chisquare.c +++ b/src/language/stats/chisquare.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -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); diff --git a/src/language/stats/crosstabs.q b/src/language/stats/crosstabs.q index 99fa41d9..5695ed64 100644 --- a/src/language/stats/crosstabs.q +++ b/src/language/stats/crosstabs.q @@ -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. */ diff --git a/src/language/stats/descriptives.c b/src/language/stats/descriptives.c index 7b30a601..d87410fb 100644 --- a/src/language/stats/descriptives.c +++ b/src/language/stats/descriptives.c @@ -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")); diff --git a/src/language/stats/examine.q b/src/language/stats/examine.q index e8333b1c..f415d003 100644 --- a/src/language/stats/examine.q +++ b/src/language/stats/examine.q @@ -49,7 +49,7 @@ #include #include #include -#include +#include #include #include @@ -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,12 @@ 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->parent; 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, + order_stats_accumulate ( &os, 1, casereader_clone (metric->up_reader), wv, dependent_vars[v], MV_ANY); } @@ -1242,10 +1131,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 +1368,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 +1547,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 +1680,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, @@ -1996,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, @@ -2079,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) { diff --git a/src/language/stats/frequencies.q b/src/language/stats/frequencies.q index 83b864c9..76ba5d3f 100644 --- a/src/language/stats/frequencies.q +++ b/src/language/stats/frequencies.q @@ -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_) /* 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. */ @@ -1062,12 +1076,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 + 3, 0); + t = tab_create (5 + lab, n_categories + 3); tab_headers (t, 0, 0, 2, 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, 1, TAB_CENTER | TAT_TITLE, _("Value Label")); @@ -1138,20 +1157,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. */ @@ -1170,7 +1195,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")); @@ -1178,7 +1203,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++) @@ -1208,7 +1233,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); } @@ -1376,8 +1401,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) ; @@ -1418,7 +1443,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 @@ -1437,7 +1462,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; @@ -1459,10 +1484,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; } @@ -1494,7 +1519,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; @@ -1511,14 +1536,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); } diff --git a/src/language/stats/npar-summary.c b/src/language/stats/npar-summary.c index d626dffa..8e6f11a8 100644 --- a/src/language/stats/npar-summary.c +++ b/src/language/stats/npar-summary.c @@ -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); diff --git a/src/language/stats/npar.q b/src/language/stats/npar.q index bbccce67..8c6f98f8 100644 --- a/src/language/stats/npar.q +++ b/src/language/stats/npar.q @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -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]); diff --git a/src/language/stats/oneway.q b/src/language/stats/oneway.q index 0f6b20a1..ddb84d89 100644 --- a/src/language/stats/oneway.q +++ b/src/language/stats/oneway.q @@ -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, diff --git a/src/language/stats/regression.q b/src/language/stats/regression.q index 2c259d0f..34bdc6f0 100644 --- a/src/language/stats/regression.q +++ b/src/language/stats/regression.q @@ -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); diff --git a/src/language/stats/reliability.q b/src/language/stats/reliability.q index 681669d1..dfb81367 100644 --- a/src/language/stats/reliability.q +++ b/src/language/stats/reliability.q @@ -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")); diff --git a/src/language/stats/roc.c b/src/language/stats/roc.c index 1d61a55c..1d21a4f6 100644 --- a/src/language/stats/roc.c +++ b/src/language/stats/roc.c @@ -16,6 +16,8 @@ #include +#include + #include #include #include @@ -36,8 +38,8 @@ #include #include -#include -#include +#include +#include #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 index 00000000..5d63c96f --- /dev/null +++ b/src/language/stats/roc.h @@ -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 . */ + +#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 */ diff --git a/src/language/stats/sign.c b/src/language/stats/sign.c index a5a27212..d5970c6f 100644 --- a/src/language/stats/sign.c +++ b/src/language/stats/sign.c @@ -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, diff --git a/src/language/stats/t-test.q b/src/language/stats/t-test.q index d02cdb28..8d2b2122 100644 --- a/src/language/stats/t-test.q +++ b/src/language/stats/t-test.q @@ -475,13 +475,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); } /* ssbox implementations. */ @@ -1067,11 +1067,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 */ @@ -1091,14 +1091,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 */ diff --git a/src/language/stats/wilcoxon.c b/src/language/stats/wilcoxon.c index 310206c0..c7e30277 100644 --- a/src/language/stats/wilcoxon.c +++ b/src/language/stats/wilcoxon.c @@ -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); diff --git a/src/language/syntax-file.c b/src/language/syntax-file.c index 0771ade3..9b5fd043 100644 --- a/src/language/syntax-file.c +++ b/src/language/syntax-file.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -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; } diff --git a/src/language/syntax-string-source.c b/src/language/syntax-string-source.c index 94a56f97..405141cd 100644 --- a/src/language/syntax-string-source.c +++ b/src/language/syntax-string-source.c @@ -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 +#include #include #include #include @@ -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. diff --git a/src/language/utilities/echo.c b/src/language/utilities/echo.c index 19e9f9e5..3d3c2c40 100644 --- a/src/language/utilities/echo.c +++ b/src/language/utilities/echo.c @@ -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))); diff --git a/src/language/xforms/count.c b/src/language/xforms/count.c index 8ce2d125..4fb889b1 100644 --- a/src/language/xforms/count.c +++ b/src/language/xforms/count.c @@ -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; } diff --git a/src/libpspp/abt.c b/src/libpspp/abt.c index 74b2ceb3..b7766129 100644 --- a/src/libpspp/abt.c +++ b/src/libpspp/abt.c @@ -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 +#include #include @@ -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); } } diff --git a/src/libpspp/abt.h b/src/libpspp/abt.h index 3801eb86..24d7268a 100644 --- a/src/libpspp/abt.h +++ b/src/libpspp/abt.h @@ -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 @@ -150,12 +150,14 @@ tree paper. */ #include +#include /* 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 diff --git a/src/libpspp/bt.c b/src/libpspp/bt.c index 26eb982b..751c8fe7 100644 --- a/src/libpspp/bt.c +++ b/src/libpspp/bt.c @@ -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 #include +#include + 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); } } diff --git a/src/libpspp/bt.h b/src/libpspp/bt.h index 340b8760..7045632e 100644 --- a/src/libpspp/bt.h +++ b/src/libpspp/bt.h @@ -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 @@ -24,12 +24,14 @@ fully encapsulated. */ #include +#include /* 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 index 00000000..1e33857c --- /dev/null +++ b/src/libpspp/cast.h @@ -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 . */ + +#ifndef LIBPSPP_CAST_H +#define LIBPSPP_CAST_H 1 + +#include + +/* 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 */ diff --git a/src/libpspp/hash.h b/src/libpspp/hash.h index 57fc2678..e586bcc0 100644 --- a/src/libpspp/hash.h +++ b/src/libpspp/hash.h @@ -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 diff --git a/src/libpspp/heap.h b/src/libpspp/heap.h index 13422437..3a3c516a 100644 --- a/src/libpspp/heap.h +++ b/src/libpspp/heap.h @@ -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 #include #include @@ -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. */ diff --git a/src/libpspp/hmap.h b/src/libpspp/hmap.h index e73d84fd..c9e764de 100644 --- a/src/libpspp/hmap.h +++ b/src/libpspp/hmap.h @@ -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 @@ -116,12 +116,14 @@ */ #include +#include /* 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. */ diff --git a/src/libpspp/ll.c b/src/libpspp/ll.c index b9d595bc..e6533f2c 100644 --- a/src/libpspp/ll.c +++ b/src/libpspp/ll.c @@ -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); } diff --git a/src/libpspp/ll.h b/src/libpspp/ll.h index 65ecf55f..bf871f6b 100644 --- a/src/libpspp/ll.h +++ b/src/libpspp/ll.h @@ -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 #include #include +#include + +#include /* Embedded, circular doubly linked list. @@ -108,8 +111,9 @@ /* 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, diff --git a/src/libpspp/llx.c b/src/libpspp/llx.c index 4dc82bff..c58f840c 100644 --- a/src/libpspp/llx.c +++ b/src/libpspp/llx.c @@ -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); } /* Allocates and returns a node using malloc. */ diff --git a/src/libpspp/range-map.h b/src/libpspp/range-map.h index 3dd77c9f..024fe937 100644 --- a/src/libpspp/range-map.h +++ b/src/libpspp/range-map.h @@ -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 @@ -33,12 +33,14 @@ #include #include +#include /* 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 diff --git a/src/libpspp/range-set.c b/src/libpspp/range-set.c index efa3b4d6..cd17fe02 100644 --- a/src/libpspp/range-set.c +++ b/src/libpspp/range-set.c @@ -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; diff --git a/src/libpspp/range-set.h b/src/libpspp/range-set.h index 941692b4..ee7dac23 100644 --- a/src/libpspp/range-set.h +++ b/src/libpspp/range-set.h @@ -26,6 +26,7 @@ #include #include +#include /* 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)); } diff --git a/src/libpspp/sparse-array.c b/src/libpspp/sparse-array.c index 28398d5c..298f2caf 100644 --- a/src/libpspp/sparse-array.c +++ b/src/libpspp/sparse-array.c @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -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) diff --git a/src/libpspp/str.c b/src/libpspp/str.c index afe32de9..8243aa06 100644 --- a/src/libpspp/str.c +++ b/src/libpspp/str.c @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -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'; diff --git a/src/libpspp/taint.c b/src/libpspp/taint.c index 3a74587b..4c1cecb9 100644 --- a/src/libpspp/taint.c +++ b/src/libpspp/taint.c @@ -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 #include +#include #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) { diff --git a/src/libpspp/tmpfile.c b/src/libpspp/tmpfile.c index 0d309364..aff135d0 100644 --- a/src/libpspp/tmpfile.c +++ b/src/libpspp/tmpfile.c @@ -26,6 +26,7 @@ #include #include +#include #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) diff --git a/src/libpspp/tower.c b/src/libpspp/tower.c index e8d253d0..91579877 100644 --- a/src/libpspp/tower.c +++ b/src/libpspp/tower.c @@ -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 #include +#include #include 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)); diff --git a/src/libpspp/tower.h b/src/libpspp/tower.h index 246984a2..9be8231c 100644 --- a/src/libpspp/tower.h +++ b/src/libpspp/tower.h @@ -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 @@ -51,12 +51,14 @@ #include #include +#include /* 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 diff --git a/src/math/box-whisker.c b/src/math/box-whisker.c index 288fc072..de4124ef 100644 --- a/src/math/box-whisker.c +++ b/src/math/box-whisker.c @@ -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 #include +#include #include #include #include @@ -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; } diff --git a/src/math/box-whisker.h b/src/math/box-whisker.h index 5202b646..bef091e8 100644 --- a/src/math/box-whisker.h +++ b/src/math/box-whisker.h @@ -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]); diff --git a/src/math/histogram.c b/src/math/histogram.c index 67079398..3c88c385 100644 --- a/src/math/histogram.c +++ b/src/math/histogram.c @@ -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 #include +#include #include #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; } diff --git a/src/math/histogram.h b/src/math/histogram.h index b2b204ee..bc4a5ae6 100644 --- a/src/math/histogram.h +++ b/src/math/histogram.h @@ -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); diff --git a/src/math/levene.c b/src/math/levene.c index 7bd58210..f7e26999 100644 --- a/src/math/levene.c +++ b/src/math/levene.c @@ -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 ); } diff --git a/src/math/np.c b/src/math/np.c index e61bf58e..b6318209 100644 --- a/src/math/np.c +++ b/src/math/np.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -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; } diff --git a/src/math/np.h b/src/math/np.h index 7db51f73..b5265bd4 100644 --- a/src/math/np.h +++ b/src/math/np.h @@ -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 diff --git a/src/math/order-stats.c b/src/math/order-stats.c index 1b6aa131..e550d2b2 100644 --- a/src/math/order-stats.c +++ b/src/math/order-stats.c @@ -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]; diff --git a/src/math/percentiles.c b/src/math/percentiles.c index bf99de16..c76bb492 100644 --- a/src/math/percentiles.c +++ b/src/math/percentiles.c @@ -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 +#include #include #include #include @@ -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; } diff --git a/src/math/percentiles.h b/src/math/percentiles.h index 0dd09820..ff46bea6 100644 --- a/src/math/percentiles.h +++ b/src/math/percentiles.h @@ -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); diff --git a/src/math/trimmed-mean.c b/src/math/trimmed-mean.c index da3d4240..d1cc6b70 100644 --- a/src/math/trimmed-mean.c +++ b/src/math/trimmed-mean.c @@ -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 #include +#include #include #include @@ -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; } diff --git a/src/math/trimmed-mean.h b/src/math/trimmed-mean.h index 9339cab9..c667b1be 100644 --- a/src/math/trimmed-mean.h +++ b/src/math/trimmed-mean.h @@ -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 diff --git a/src/math/tukey-hinges.c b/src/math/tukey-hinges.c index 95a79c1d..22ab4521 100644 --- a/src/math/tukey-hinges.c +++ b/src/math/tukey-hinges.c @@ -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 #include +#include #include 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; } diff --git a/src/math/tukey-hinges.h b/src/math/tukey-hinges.h index d87691f8..4b509da1 100644 --- a/src/math/tukey-hinges.h +++ b/src/math/tukey-hinges.h @@ -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]); diff --git a/src/output/ascii.c b/src/output/ascii.c index 29d7b76b..b224b3eb 100644 --- a/src/output/ascii.c +++ b/src/output/ascii.c @@ -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,11 +29,12 @@ #include #include #include +#include +#include +#include -#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 }; diff --git a/src/output/automake.mk b/src/output/automake.mk index ec92c559..eb9f7b78 100644 --- a/src/output/automake.mk +++ b/src/output/automake.mk @@ -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 index 00000000..b0b4f7d3 --- /dev/null +++ b/src/output/cairo.c @@ -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 . */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + +/* 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; +} + +/* 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); +} + +/* 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); +} + +/* 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); +} + +/* 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 index 00000000..c4f8a813 --- /dev/null +++ b/src/output/cairo.h @@ -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 . */ + +#ifndef OUTPUT_CAIRO_H +#define OUTPUT_CAIRO_H 1 + +#include + +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 index 00000000..9becb6f5 --- /dev/null +++ b/src/output/chart-provider.h @@ -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 . */ + +#ifndef OUTPUT_CHART_PROVIDER_H +#define OUTPUT_CHART_PROVIDER_H 1 + +#include +#include +#include +#include + +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 */ diff --git a/src/output/chart.c b/src/output/chart.c index e324901c..e31422bc 100644 --- a/src/output/chart.c +++ b/src/output/chart.c @@ -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 @@ -17,8 +17,10 @@ #include #include +#include #include +#include #include #include #include @@ -27,8 +29,6 @@ #include #include -#include - #include #include #include @@ -41,128 +41,141 @@ 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); } diff --git a/src/output/chart.h b/src/output/chart.h index 4da12ecf..d6b9b3d4 100644 --- a/src/output/chart.h +++ b/src/output/chart.h @@ -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 @@ -14,85 +14,16 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include -#include -#include -#include -#include -#include -#include +#ifndef OUTPUT_CHART_H +#define OUTPUT_CHART_H 1 -#include -#include -#include "manager.h" -#include "output.h" +#include -#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 -#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 index ab0ff510..00000000 --- a/src/output/charts/automake.mk +++ /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 index 88a1d741..00000000 --- a/src/output/charts/barchart.c +++ /dev/null @@ -1,252 +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 . */ - - -#include - -#include -#include -#include -#include -#include -#include -#include - -#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 ), - 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, 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 index 09832726..00000000 --- a/src/output/charts/barchart.h +++ /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 . */ - -#ifndef BARCHART_H -#define BARCHART_H - -#include - -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 diff --git a/src/output/charts/box-whisker.c b/src/output/charts/box-whisker.c index c3641580..5fd9d295 100644 --- a/src/output/charts/box-whisker.c +++ b/src/output/charts/box-whisker.c @@ -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 @@ -17,49 +17,95 @@ #include +#include + #include #include -#include - -#include -#include +#include -#include +#include +#include #include #include +#include +#include +#include /* 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, 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 + }; diff --git a/src/output/charts/box-whisker.h b/src/output/charts/box-whisker.h index 7b2c4b8f..67c1e128 100644 --- a/src/output/charts/box-whisker.h +++ b/src/output/charts/box-whisker.h @@ -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 @@ -17,16 +17,11 @@ #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 diff --git a/src/output/charts/cartesian.c b/src/output/charts/cartesian.c index cb2f346b..eabcf516 100644 --- a/src/output/charts/cartesian.c +++ b/src/output/charts/cartesian.c @@ -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 @@ -17,86 +17,76 @@ #include +#include + +#include #include #include #include - +#include #include -#include #include +#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); } diff --git a/src/output/charts/cartesian.h b/src/output/charts/cartesian.h index 0874b9cc..3c21db6e 100644 --- a/src/output/charts/cartesian.h +++ b/src/output/charts/cartesian.h @@ -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 +#include +#include 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 index e22f958f..00000000 --- a/src/output/charts/dummy-chart.c +++ /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 . */ - - -/* Stubs for plotting routines. - This module is linked only when charts are not supported */ - -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#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 index 00000000..c077b872 --- /dev/null +++ b/src/output/charts/np-plot.c @@ -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 . */ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 00000000..c9742359 --- /dev/null +++ b/src/output/charts/np-plot.h @@ -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 . */ + +#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 */ diff --git a/src/output/charts/piechart.c b/src/output/charts/piechart.c index 4eeb10ca..935c6eb0 100644 --- a/src/output/charts/piechart.c +++ b/src/output/charts/piechart.c @@ -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 @@ -17,74 +17,101 @@ #include -#include +#include + #include +#include +#include #include #include - -#include -#include - -#include -#include #include +#include +#include +#include +#include #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 = chart_create(); + 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]; - const double left_label = ch->data_left + - (ch->data_right - ch->data_left)/10.0; + ds_init_string (&dst->label, &src->label); + dst->magnitude = src->magnitude; + } + pie->n_slices = n_slices; + return &pie->chart; +} - const double right_label = ch->data_right - - (ch->data_right - ch->data_left)/10.0; +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; - const double centre_x = (ch->data_right + ch->data_left ) / 2.0 ; - const double centre_y = (ch->data_top + ch->data_bottom ) / 2.0 ; + centre_x = (geom->data_right + geom->data_left) / 2.0 ; + centre_y = (geom->data_top + geom->data_bottom) / 2.0 ; - const double 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; + radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom), + 1.0 / 4.0 * (geom->data_right - geom->data_left)); - chart_write_title(ch, 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; + chart_write_title (cr, geom, "%s", pie->title); - for (i = 0 ; i < n_slices ; ++i ) - { - static double angle=0.0; + 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); @@ -93,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 + }; diff --git a/src/output/charts/piechart.h b/src/output/charts/piechart.h index 96540401..39a0c2d5 100644 --- a/src/output/charts/piechart.h +++ b/src/output/charts/piechart.h @@ -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 @@ -21,12 +21,11 @@ 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 diff --git a/src/output/charts/plot-chart.c b/src/output/charts/plot-chart.c index 5641db12..46884e19 100644 --- a/src/output/charts/plot-chart.c +++ b/src/output/charts/plot-chart.c @@ -16,241 +16,321 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include - #include -#include - - +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include #include +#include +#include +#include #include #include #include "xalloc.h" -const char *const data_colour[N_CHART_COLOURS] = +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); - - pl_move_r(chart->lp, chart->data_left, chart->data_bottom); + cairo_move_to (cr, geom->data_left, geom->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); - - 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); - } + cairo_stroke (cr); - 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); - - assert ( ch ); - + chart_rounded_tick ((max - min) / (double) ticks); - 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); + chart_rounded_tick ((smax - smin) / (double) ticks); - if ( !ch ) - return; + geom->y_max = ceil (smax / tick_interval) * tick_interval; + geom->y_min = floor (smin / tick_interval) * tick_interval; - ch->y_max = ceil ( smax / tick_interval ) * tick_interval ; - ch->y_min = floor ( smin / tick_interval ) * tick_interval ; + geom->ordinate_scale = + (fabs (geom->data_top - geom->data_bottom) + / fabs (geom->y_max - geom->y_min)); - ch->ordinate_scale = - fabs(ch->data_top - ch->data_bottom) / fabs(ch->y_max - ch->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 ); + (vstep * geom->n_datasets + 2 * ypad ); - if ( ! ch ) - return ; + cairo_save (cr); - pl_savestate_r (ch->lp); + cairo_rectangle (cr, geom->legend_left, legend_top, + geom->legend_right - xpad - geom->legend_left, + legend_bottom - legend_top); + cairo_stroke (cr); - pl_box_r (ch->lp, ch->legend_left, legend_top, - ch->legend_right - xpad, legend_bottom); - - 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); } diff --git a/src/output/charts/plot-chart.h b/src/output/charts/plot-chart.h index f4cc5bbf..896b630b 100644 --- a/src/output/charts/plot-chart.h +++ b/src/output/charts/plot-chart.h @@ -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 @@ -14,6 +14,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#ifndef PLOT_CHART_H +#define PLOT_CHART_H + +#include #include #include #include @@ -25,18 +29,15 @@ #include #include +#include +#include #include #include #include -#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 { @@ -44,32 +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, ...); + const char *label, ...) + PRINTF_FORMAT (5, 6); /* Write the title on a chart*/ -void chart_write_title(struct chart *chart, const char *title, ...); +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 diff --git a/src/output/charts/plot-hist.c b/src/output/charts/plot-hist.c index 4b11618a..db3d89ba 100644 --- a/src/output/charts/plot-hist.c +++ b/src/output/charts/plot-hist.c @@ -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 @@ -18,7 +18,6 @@ #include #include -#include #include #include #include @@ -26,8 +25,10 @@ #include #include +#include #include +#include #include #include #include @@ -36,161 +37,187 @@ #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); - - { - char buf[5]; - snprintf (buf,5,"%g", (upper + lower) / 2.0); - draw_tick (ch, TICK_ABSCISSA, - x_pos + width / 2.0, buf); - } - } + 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, ¬_used); + gsl_histogram_get_range (h->gsl_hist, 0, &x_min, ¬_used); range = not_used - x_min; - gsl_histogram_get_range (hist->gsl_hist, bins - 1, ¬_used, &x_max); + gsl_histogram_get_range (h->gsl_hist, bins - 1, ¬_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 + }; diff --git a/src/output/charts/plot-hist.h b/src/output/charts/plot-hist.h index 606206d5..1e5b59ed 100644 --- a/src/output/charts/plot-hist.h +++ b/src/output/charts/plot-hist.h @@ -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 @@ -14,26 +14,22 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#ifndef PLOT_HIST_H -#define PLOT_HIST_H +#ifndef OUTPUT_PLOT_HIST_H +#define OUTPUT_PLOT_HIST_H #include 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 index 00000000..2094ede5 --- /dev/null +++ b/src/output/charts/roc-chart.c @@ -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 . */ + +#include + +#include + +#include +#include +#include +#include +#include + +#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 index 00000000..dca84207 --- /dev/null +++ b/src/output/charts/roc-chart.h @@ -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 . */ + +#ifndef OUTPUT_CHARTS_ROC_CHART_H +#define OUTPUT_CHARTS_ROC_CHART_H 1 + +#include + +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 */ diff --git a/src/output/html.c b/src/output/html.c index 893c1852..dfa524a7 100644 --- a/src/output/html.c +++ b/src/output/html.c @@ -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 @@ -27,12 +27,13 @@ #include #include #include -#include "error.h" -#include "output.h" -#include "manager.h" -#include "table.h" +#include +#include +#include +#include #include +#include "error.h" #include "xalloc.h" #include "gettext.h" @@ -47,23 +48,25 @@ 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, "", 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, "", 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 ("

", 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 (" \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 or 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 ("\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 }; diff --git a/src/output/manager.c b/src/output/manager.c index 0b2fdfe1..9b901393 100644 --- a/src/output/manager.c +++ b/src/output/manager.c @@ -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 @@ -15,16 +15,42 @@ along with this program. If not, see . */ #include -#include "manager.h" + +#include + #include #include + #include -#include "output.h" +#include + +#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; +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; } -/* 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; } diff --git a/src/output/manager.h b/src/output/manager.h index 58d0c12d..e7276982 100644 --- a/src/output/manager.h +++ b/src/output/manager.h @@ -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 index 00000000..8471b6b3 --- /dev/null +++ b/src/output/odt.c @@ -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 . */ + +#include + +#include "gettext.h" +#define _(msgid) gettext (msgid) + +/* A driver for creating OpenDocument Format text files from PSPP's output */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#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, +}; diff --git a/src/output/output.c b/src/output/output.c index 843b0d4e..a556ad81 100644 --- a/src/output/output.c +++ b/src/output/output.c @@ -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 @@ -39,12 +39,6 @@ #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; } diff --git a/src/output/output.h b/src/output/output.h index fc668740..8ae5c04b 100644 --- a/src/output/output.h +++ b/src/output/output.h @@ -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 #include - /* 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 */ diff --git a/src/output/postscript.c b/src/output/postscript.c index 11116b9b..394ebbb2 100644 --- a/src/output/postscript.c +++ b/src/output/postscript.c @@ -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 #include +#include #include #include #include @@ -32,15 +33,13 @@ #include #include #include +#include +#include +#include +#include +#include -#include - -#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); } -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 -} - 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 }; diff --git a/src/output/table.c b/src/output/table.c index 72edf17f..f8736e62 100644 --- a/src/output/table.c +++ b/src/output/table.c @@ -37,6 +37,7 @@ #include +#include "error.h" #include "minmax.h" #include "xalloc.h" @@ -44,7 +45,6 @@ #define _(msgid) gettext (msgid) 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; } /* 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); } @@ -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; -} /* 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); } -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; -} diff --git a/src/output/table.h b/src/output/table.h index 1748c24c..f4fbc786 100644 --- a/src/output/table.h +++ b/src/output/table.h @@ -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 */ diff --git a/src/ui/automake.mk b/src/ui/automake.mk index c21b681d..743d0b6d 100644 --- a/src/ui/automake.mk +++ b/src/ui/automake.mk @@ -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 diff --git a/src/ui/gui/automake.mk b/src/ui/gui/automake.mk index 110ef43f..838f0d69 100644 --- a/src/ui/gui/automake.mk +++ b/src/ui/gui/automake.mk @@ -29,6 +29,7 @@ src_ui_gui_psppire_LDADD = \ src/libpspp.la \ src/libpspp-core.la \ $(GTK_LIBS) \ + $(CAIRO_LIBS) \ @LIBINTL@ src_ui_gui_psppiredir = $(pkgdatadir) diff --git a/src/ui/gui/executor.c b/src/ui/gui/executor.c index 9336f7ce..aa2fae51 100644 --- a/src/ui/gui/executor.c +++ b/src/ui/gui/executor.c @@ -104,7 +104,5 @@ execute_syntax (struct getl_interface *sss) som_flush (); - psppire_output_window_reload (); - return retval; } diff --git a/src/ui/gui/find-dialog.c b/src/ui/gui/find-dialog.c index 11ec0603..0c16bfa8 100644 --- a/src/ui/gui/find-dialog.c +++ b/src/ui/gui/find-dialog.c @@ -34,6 +34,7 @@ which match particular strings */ #include #include #include +#include #include #include @@ -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; diff --git a/src/ui/gui/output-viewer.glade b/src/ui/gui/output-viewer.glade index 0b709ee4..50d92d46 100644 --- a/src/ui/gui/output-viewer.glade +++ b/src/ui/gui/output-viewer.glade @@ -1,7 +1,7 @@ - - - + + + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 600 @@ -16,7 +16,6 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK _File @@ -27,18 +26,18 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-save True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - gtk-save True True + gtk-save-as True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - gtk-save-as True True @@ -49,7 +48,6 @@ - False False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK _Edit @@ -60,9 +58,9 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + gtk-copy True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - gtk-copy True True @@ -121,23 +119,52 @@ False + 0 - + True True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC + 112 + True - + + True + True + automatic + automatic + + + True + True + False + + + + + False + True + + + + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - False - 5 + automatic + automatic + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + True + True + diff --git a/src/ui/gui/psppire-output-window.c b/src/ui/gui/psppire-output-window.c index b0eff950..35216d4e 100644 --- a/src/ui/gui/psppire-output-window.c +++ b/src/ui/gui/psppire-output-window.c @@ -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 @@ -21,6 +21,10 @@ #include "helper.h" #include +#include +#include +#include +#include #include #include "about.h" @@ -38,7 +42,12 @@ #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) { } - - +/* 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, + >k_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)); +} + 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, + >k_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); -} - - - diff --git a/src/ui/gui/psppire-output-window.h b/src/ui/gui/psppire-output-window.h index d11eca61..606a9504 100644 --- a/src/ui/gui/psppire-output-window.h +++ b/src/ui/gui/psppire-output-window.h @@ -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; /* */ - 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 diff --git a/src/ui/gui/psppire.c b/src/ui/gui/psppire.c index 3555463f..928a1c92 100644 --- a/src/ui/gui/psppire.c +++ b/src/ui/gui/psppire.c @@ -121,25 +121,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); @@ -339,16 +321,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; -} diff --git a/src/ui/gui/syntax-editor-source.c b/src/ui/gui/syntax-editor-source.c index 21437051..6ec866c9 100644 --- a/src/ui/gui/syntax-editor-source.c +++ b/src/ui/gui/syntax-editor-source.c @@ -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 #include +#include #include #include @@ -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; } diff --git a/src/ui/terminal/automake.mk b/src/ui/terminal/automake.mk index 5ab09857..63b83384 100644 --- a/src/ui/terminal/automake.mk +++ b/src/ui/terminal/automake.mk @@ -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@ diff --git a/src/ui/terminal/read-line.c b/src/ui/terminal/read-line.c index 7b23f38d..f85e4e76 100644 --- a/src/ui/terminal/read-line.c +++ b/src/ui/terminal/read-line.c @@ -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 #include #include +#include #include #include #include @@ -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; } diff --git a/tests/libpspp/range-set-test.c b/tests/libpspp/range-set-test.c index e8cbffa5..8d6e6831 100644 --- a/tests/libpspp/range-set-test.c +++ b/tests/libpspp/range-set-test.c @@ -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);