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
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
PSPP NEWS -- history of user-visible changes.
-Time-stamp: <2009-09-08 21:08:29 blp>
+Time-stamp: <2009-10-24 08:12:04 blp>
Copyright (C) 1996-9, 2000, 2008, 2009 Free Software Foundation, Inc.
See the end for copying conditions.
Please send PSPP bug reports to bug-gnu-pspp@gnu.org.
+Changes from 0.7.2 to 0.7.3:
+
+ * Charts are now produced with Cairo and Pango, instead of libplot.
+ Without them, the new graphing features will not work. If you do
+ not have Cairo and Pango installed, you must run `configure' with
+ --without-cairo.
+
Changes from 0.7.1 to 0.7.2:
* Updated Perl module interface.
* 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.
AC_SUBST([VERSION_FOR_PERL])
])
-dnl Check that libplot is available.
-AC_DEFUN([PSPP_LIBPLOT],
-[
- AC_ARG_WITH(
- libplot,
- [AS_HELP_STRING([--without-libplot],
- [don't compile in support of charts (using libplot)])])
-
- if test x"$with_libplot" != x"no" ; then
- # Check whether we can link against libplot without any extra libraries.
- AC_CHECK_LIB(plot, pl_newpl_r, [LIBPLOT_LIBS="-lplot"])
-
- # Check whether we can link against libplot if we also link X.
- if test x"$LIBPLOT_LIBS" = x""; then
- AC_PATH_XTRA
- extra_libs="-lXaw -lXmu -lXt $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS -lm"
- AC_CHECK_LIB(plot, pl_newpl_r,
- [LIBPLOT_LIBS="-lplot $extra_libs"
- LDFLAGS="$LDFLAGS $X_LIBS"],,
- [$extra_libs])
- fi
-
- # Still can't link?
- if test x"$LIBPLOT_LIBS" = x""; then
- PSPP_REQUIRED_PREREQ([libplot (or use --without-libplot)])
- fi
-
- # Set up to make everything work.
- LIBS="$LIBPLOT_LIBS $LIBS"
- AC_DEFINE(HAVE_LIBPLOT, 1,
- [Define to 1 if you have the `libplot' library (-lplot).])
- fi
-])
-
dnl PSPP_CHECK_CC_OPTION([OPTION], [ACTION-IF-ACCEPTED], [ACTION-IF-REJECTED])
dnl Check whether the given C compiler OPTION is accepted.
dnl If so, execute ACTION-IF-ACCEPTED, otherwise ACTION-IF-REJECTED.
# 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' \
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:
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
dnl Checks for libraries.
AC_SYS_LARGEFILE
AC_SEARCH_LIBS([sin], [m])
-PSPP_LIBPLOT
PSPP_LC_PAPER
-AM_CONDITIONAL(WITHCHARTS, test x"$with_libplot" != x"no")
AC_ARG_VAR([PSPP_LDFLAGS], [linker flags to be used for linking the pspp binary only])
AC_ARG_VAR([PSPPIRE_LDFLAGS], [linker flags to be used for linking the psppire binary only])
-
-AC_ARG_WITH(
- gui,
- [AS_HELP_STRING([--without-gui], [don't build the PSPPIRE gui])])
-
-required_gtk_version=2.12
-
-if test x"$with_gui" != x"no" ; then
- PKG_CHECK_MODULES(GTK, gtk+-2.0 >= $required_gtk_version,,
- [PSPP_REQUIRED_PREREQ([gtk+ 2.0 v$required_gtk_version or later (or use --without-gui)])])
+# Support for Cairo and Pango.
+AC_ARG_WITH([cairo],
+ [AS_HELP_STRING(
+ [--without-cairo],
+ [Don't build support for charts (using Cairo and Pango);
+ implies --without-gui])],
+ [], [with_cairo=yes])
+AM_CONDITIONAL([HAVE_CAIRO], [test "$with_cairo" != no])
+if test "$with_cairo" != no; then
+ PKG_CHECK_MODULES([CAIRO], [cairo >= 1.5 pango >= 1.20 pangocairo],
+ [CPPFLAGS="$CPPFLAGS $CAIRO_CFLAGS"
+ AC_DEFINE([HAVE_CAIRO], 1,
+ [Define to 1 if Cairo and Pango are available.])],
+ [PSPP_REQUIRED_PREREQ([cairo 1.5 or later and pango 1.20 or later (or use --without-cairo)])])
fi
-AM_CONDITIONAL(WITHGUI, test x"$with_gui" != x"no")
+# Support for GUI.
+AC_ARG_WITH([gui],
+ [AS_HELP_STRING([--without-gui],
+ [Don't build the PSPPIRE GUI (using GTK+)])],
+ [], [with_gui=yes])
+AM_CONDITIONAL([HAVE_GUI],
+ [test "$with_cairo" != no && test "$with_gui" != "no"])
+if test "$with_cairo" != no && test "$with_gui" != "no"; then
+ PKG_CHECK_MODULES([GTK], [gtk+-2.0 >= 2.12], [],
+ [PSPP_REQUIRED_PREREQ([gtk+ 2.0 version 2.12 or later (or use --without-gui)])])
+fi
dnl Checks needed for psql reader
* 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
@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:
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}
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}
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}
@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}
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
include $(top_srcdir)/lib/linreg/automake.mk
-if WITHGUI
+if HAVE_GUI
include $(top_srcdir)/lib/gtk-contrib/automake.mk
endif
=cut
BEGIN {
- $PSPP::VERSION='0.7.2';
+ $PSPP::VERSION='0.7.3';
require XSLoader;
XSLoader::load('PSPP', $PSPP::VERSION);
}
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);
#include <stddef.h>
#include <stdlib.h>
#include <data/value.h>
+#include <libpspp/cast.h>
#include <libpspp/compiler.h>
/* Case prototype.
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;
}
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;
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))
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;
{
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
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
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;
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. */
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));
}
}
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)
{
#include <data/value.h>
#include <data/variable.h>
#include <libpspp/array.h>
+#include <libpspp/cast.h>
#include <libpspp/compiler.h>
#include <libpspp/hash-functions.h>
#include <libpspp/hmap.h>
val_labs_replace (struct val_labs *vls, const union value *value,
const char *label)
{
- struct val_lab *vl = (struct val_lab *) val_labs_lookup (vls, value);
+ struct val_lab *vl = val_labs_lookup (vls, value);
if (vl != NULL)
{
atom_destroy (vl->label);
/* Removes LABEL from VLS. */
void
-val_labs_remove (struct val_labs *vls, const struct val_lab *label_)
+val_labs_remove (struct val_labs *vls, struct val_lab *label)
{
- struct val_lab *label = (struct val_lab *) label_;
hmap_delete (&vls->labels, &label->node);
value_destroy (&label->value, vls->width);
atom_destroy (label->label);
/* Searches VLS for a value label for VALUE. If successful,
returns the value label; otherwise, returns a null pointer.
Returns a null pointer if VLS is null. */
-const struct val_lab *
+struct val_lab *
val_labs_lookup (const struct val_labs *vls, const union value *value)
{
if (vls != NULL)
/* Looking up value labels. */
const char *val_labs_find (const struct val_labs *, const union value *);
-const struct val_lab *val_labs_lookup (const struct val_labs *,
+struct val_lab *val_labs_lookup (const struct val_labs *,
const union value *);
/* Basic properties. */
/* Adding value labels. */
bool val_labs_add (struct val_labs *, const union value *, const char *);
void val_labs_replace (struct val_labs *, const union value *, const char *);
-void val_labs_remove (struct val_labs *, const struct val_lab *);
+void val_labs_remove (struct val_labs *, struct val_lab *);
/* Iterating through value labels. */
const struct val_lab *val_labs_first (const struct val_labs *);
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;
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;
}
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. */
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 \
#include <libpspp/message.h>
#include <libpspp/str.h>
#include <output/manager.h>
-#include <output/table.h>
#include <libpspp/getl.h>
#if HAVE_SYS_WAIT_H
/* 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));
/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <language/command.h>
#include <language/lexer/lexer.h>
#include <language/lexer/variable-parser.h>
+#include <libpspp/cast.h>
#include <libpspp/ll.h>
#include <libpspp/message.h>
#include <libpspp/misc.h>
/* 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;
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);
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);
/* 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);
}
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"));
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++)
{
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++)
{
#include <language/dictionary/split-file.h>
#include <language/lexer/lexer.h>
#include <libpspp/compiler.h>
-#include <libpspp/message.h>
+#include <libpspp/ll.h>
#include <libpspp/message.h>
#include <libpspp/misc.h>
#include <output/htmlP.h>
/* (functions) */
/* Layout for one output driver. */
-struct list_ext
+struct list_target
{
+ struct ll ll;
+ struct outp_driver *driver;
int type; /* 0=Values and labels fit across the page. */
size_t n_vertical; /* Number of labels to list vertically. */
size_t header_rows; /* Number of header rows. */
/* 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. */
struct variable *casenum_var = NULL;
struct casegrouper *grouper;
struct casereader *group;
+ struct ll_list targets;
casenumber case_idx;
bool ok;
cmd.v_variables[0] = casenum_var;
}
- determine_layout ();
+ determine_layout (&targets);
case_idx = 0;
for (grouper = casegrouper_create_splits (proc_open (ds), dict);
{
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);
ds_destroy(&line_buffer);
- clean_up ();
+ clean_up (&targets);
var_destroy (casenum_var);
/* 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);
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)
{
/* 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);
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;
}
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);
}
/* Write out the header, in back-to-front order except for the last line. */
- if (prc->header_rows >= 2)
+ if (target->header_rows >= 2)
{
size_t i;
- for (i = prc->header_rows - 1; i-- != 0; )
- write_line (d, prc->header[i]);
+ for (i = target->header_rows - 1; i-- != 0; )
+ write_line (d, target->header[i]);
}
- write_line (d, prc->header[prc->header_rows - 1]);
+ write_line (d, target->header[target->header_rows - 1]);
}
/* Frees up all the memory we've allocated. */
static void
-clean_up (void)
+clean_up (struct ll_list *targets)
{
- struct outp_driver *d;
+ struct list_target *target, *next;
- for (d = outp_drivers (NULL); d; d = outp_drivers (d))
- if (d->class->special == 0)
- {
- struct list_ext *prc = d->prc;
- size_t i;
-
- if (prc->header)
- {
- for (i = 0; i < prc->header_rows; i++)
- free (prc->header[i]);
- free (prc->header);
- }
- free (prc);
- }
- else if (d->class == &html_class)
- {
- if (d->page_open)
- {
- struct html_driver_ext *x = d->ext;
+ ll_for_each_safe (target, next, struct list_target, ll, targets)
+ {
+ struct outp_driver *d = target->driver;
+ if (d->class->special == 0)
+ {
+ if (target->header)
+ {
+ size_t i;
+ for (i = 0; i < target->header_rows; i++)
+ free (target->header[i]);
+ free (target->header);
+ }
+ }
+ else if (d->class == &html_class)
+ {
+ if (d->page_open)
+ {
+ struct html_driver_ext *x = d->ext;
- fputs ("</TABLE>\n", x->file);
- }
- }
- else
- NOT_REACHED ();
+ fputs ("</TABLE>\n", x->file);
+ }
+ }
+ else
+ NOT_REACHED ();
+ ll_remove (&target->ll);
+ free (target);
+ }
+
free (cmd.v_variables);
}
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;
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. */
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);
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++)
{
}
if (width <= max_width)
{
- prc->header_rows = 2;
+ target->header_rows = 2;
continue;
}
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; )
{
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);
/* Writes case C to output. */
static void
list_case (const struct ccase *c, casenumber case_idx,
- const struct dataset *ds)
+ const struct dataset *ds, struct ll_list *targets)
{
struct dictionary *dict = dataset_dict (ds);
- struct outp_driver *d;
+ const char *encoding = dict_get_encoding (dict);
+ struct list_target *target;
- for (d = outp_drivers (NULL); d; d = outp_drivers (d))
- if (d->class->special == 0)
- {
- const struct list_ext *prc = d->prc;
- const int max_width = n_chars_width (d);
- int column;
-
- if (!prc->header_rows)
- {
- ds_put_format(&line_buffer, "%8s: ",
- var_get_name (cmd.v_variables[0]));
- }
-
-
- for (column = 0; column < cmd.n_variables; column++)
- {
- const struct variable *v = cmd.v_variables[column];
- const struct fmt_spec *print = var_get_print_format (v);
- int width;
-
- if (prc->type == 0 && column >= prc->n_vertical)
- {
- int name_len = strlen (var_get_name (v));
- width = MAX (name_len, print->w);
- }
- else
- width = print->w;
-
- if (width + ds_length(&line_buffer) > max_width &&
- ds_length(&line_buffer) != 0)
- {
- if (!n_lines_remaining (d))
- {
- outp_eject_page (d);
- write_header (d);
- }
+ ll_for_each (target, struct list_target, ll, targets)
+ {
+ struct outp_driver *d = target->driver;
- write_line (d, ds_cstr (&line_buffer));
- ds_clear(&line_buffer);
+ if (d->class->special == 0)
+ {
+ const int max_width = n_chars_width (d);
+ int column;
- if (!prc->header_rows)
- ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
- }
+ if (!target->header_rows)
+ {
+ ds_put_format(&line_buffer, "%8s: ",
+ var_get_name (cmd.v_variables[0]));
+ }
- if (width > print->w)
- ds_put_char_multiple(&line_buffer, ' ', width - print->w);
- if (fmt_is_string (print->type)
- || dict_contains_var (dict, v))
- {
- char *s = data_out (case_data (c, v), dict_get_encoding (dict), print);
- ds_put_cstr (&line_buffer, s);
- free (s);
- }
- else
- {
- char *s;
- union value case_idx_value;
- case_idx_value.f = case_idx;
- s = data_out (&case_idx_value, dict_get_encoding (dict), print);
- ds_put_cstr (&line_buffer, s);
- free (s);
- }
-
- ds_put_char (&line_buffer, ' ');
- }
+ for (column = 0; column < cmd.n_variables; column++)
+ {
+ const struct variable *v = cmd.v_variables[column];
+ const struct fmt_spec *print = var_get_print_format (v);
+ int width;
+ char *s;
+
+ if (target->type == 0 && column >= target->n_vertical)
+ {
+ int name_len = strlen (var_get_name (v));
+ width = MAX (name_len, print->w);
+ }
+ else
+ width = print->w;
+
+ if (width + ds_length(&line_buffer) > max_width &&
+ ds_length(&line_buffer) != 0)
+ {
+ if (!n_lines_remaining (d))
+ {
+ outp_eject_page (d);
+ write_header (target);
+ }
+
+ write_line (d, ds_cstr (&line_buffer));
+ ds_clear(&line_buffer);
+
+ if (!target->header_rows)
+ ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
+ }
+
+ if (width > print->w)
+ ds_put_char_multiple(&line_buffer, ' ', width - print->w);
+
+ if (fmt_is_string (print->type) || dict_contains_var (dict, v))
+ s = data_out (case_data (c, v), encoding, print);
+ else
+ {
+ union value case_idx_value;
+ case_idx_value.f = case_idx;
+ s = data_out (&case_idx_value, encoding, print);
+ }
+
+ ds_put_cstr (&line_buffer, s);
+ free (s);
+ ds_put_char(&line_buffer, ' ');
+ }
- if (!n_lines_remaining (d))
- {
- outp_eject_page (d);
- write_header (d);
- }
+ if (!n_lines_remaining (d))
+ {
+ outp_eject_page (d);
+ write_header (target);
+ }
- write_line (d, ds_cstr (&line_buffer));
- ds_clear(&line_buffer);
- }
- else if (d->class == &html_class)
- {
- struct html_driver_ext *x = d->ext;
- int column;
+ write_line (d, ds_cstr (&line_buffer));
+ ds_clear(&line_buffer);
+ }
+ else if (d->class == &html_class)
+ {
+ struct html_driver_ext *x = d->ext;
+ int column;
- fputs (" <TR>\n", x->file);
+ fputs (" <TR>\n", x->file);
- for (column = 0; column < cmd.n_variables; column++)
- {
- const struct variable *v = cmd.v_variables[column];
- const struct fmt_spec *print = var_get_print_format (v);
- char *s = NULL;
-
- if (fmt_is_string (print->type)
- || dict_contains_var (dict, v))
- s = data_out (case_data (c, v), dict_get_encoding (dict), print);
- else
- {
- union value case_idx_value;
- case_idx_value.f = case_idx;
- s = data_out (&case_idx_value, dict_get_encoding (dict), print);
- }
-
- fputs (" <TD>", x->file);
- html_put_cell_contents (d, TAB_FIX, ss_buffer (s, print->w));
- free (s);
- fputs ("</TD>\n", x->file);
- }
+ for (column = 0; column < cmd.n_variables; column++)
+ {
+ const struct variable *v = cmd.v_variables[column];
+ const struct fmt_spec *print = var_get_print_format (v);
+ char *s;
+
+ if (fmt_is_string (print->type)
+ || dict_contains_var (dict, v))
+ s = data_out (case_data (c, v), encoding, print);
+ else
+ {
+ union value case_idx_value;
+ case_idx_value.f = case_idx;
+ s = data_out (&case_idx_value, encoding, print);
+ }
+
+ fputs (" <TD>", x->file);
+ html_put_cell_contents (d, TAB_FIX, ss_cstr (s));
+ fputs ("</TD>\n", x->file);
+
+ free (s);
+ }
- fputs (" </TR>\n", x->file);
- }
- else
- NOT_REACHED ();
+ fputs (" </TR>\n", x->file);
+ }
+ else
+ NOT_REACHED ();
+ }
}
+
/*
Local Variables:
mode: c
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);
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)
{
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"));
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);
/* 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. */
}
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));
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"));
}
}
-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. */
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"));
? _("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++)
if (flags & ~DF_DICT_INDEX)
tab_vline (t, TAL_1, nc - 1, 0, r - 1);
tab_resize (t, -1, r);
- tab_columns (t, TAB_COL_DOWN, 1);
+ tab_columns (t, TAB_COL_DOWN);
tab_submit (t);
}
\f
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);
}
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);
/* 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
{
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
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:
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)
{
#include <language/stats/freq.h>
#include <language/stats/npar.h>
#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
#include <libpspp/compiler.h>
#include <libpspp/hash.h>
#include <libpspp/message.h>
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"));
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);
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;
}
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 )
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;
}
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);
{
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);
const int heading_columns = 1;
const int heading_rows = 1;
- struct tab_table *t = tab_create (nc, nr, 0);
+ struct tab_table *t = tab_create (nc, nr);
tab_title (t, _("Descriptive Statistics"));
- tab_dim (t, tab_natural_dimensions, NULL);
+ tab_dim (t, tab_natural_dimensions, NULL, NULL);
tab_headers (t, heading_columns, 0, heading_rows, 0);
/* One header row */
nr += heading_rows;
- t = tab_create (nc, nr, 0);
+ t = tab_create (nc, nr);
tab_title (t, _("Correlations"));
- tab_dim (t, tab_natural_dimensions, NULL);
+ tab_dim (t, tab_natural_dimensions, NULL, NULL);
tab_headers (t, heading_columns, 0, heading_rows, 0);
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)
{
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,
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"));
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,
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. */
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."));
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."));
{
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."));
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."));
submit (struct crosstabs_proc *proc, struct pivot_table *pt,
struct tab_table *t)
{
+ struct crosstabs_dim_aux *aux;
int i;
if (t == NULL)
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;
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
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
{
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. */
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;
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"));
#include <libpspp/str.h>
#include <math/moments.h>
#include <output/charts/box-whisker.h>
-#include <output/charts/cartesian.h>
+#include <output/charts/np-plot.h>
#include <output/manager.h>
#include <output/table.h>
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;
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);
};
-/* 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
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);
}
}
}
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);
}
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))
{
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));
}
}
)
{
+ 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));
}
}
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);
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];
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),
{
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);
struct factor_metrics *metric = &result->metrics[v];
int n_vals = caseproto_get_n_widths (casereader_get_proto (
metric->up_reader));
+ struct order_stats *os;
metric->box_whisker =
- box_whisker_create ((struct tukey_hinges *) metric->tukey_hinges,
- cmd->v_id, n_vals - 1);
+ box_whisker_create ( metric->tukey_hinges, cmd->v_id, n_vals - 1);
- order_stats_accumulate ((struct order_stats **) &metric->box_whisker,
- 1,
+ os = &metric->box_whisker->parent;
+ order_stats_accumulate ( &os, 1,
casereader_clone (metric->up_reader),
wv, dependent_vars[v], MV_ANY);
}
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,
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,
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);
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,
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,
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)
{
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)
\f
/* Frequency table display. */
+struct full_dim_aux
+ {
+ bool show_labels;
+ };
+
/* Sets the widths of all the columns and heights of all the rows in
table T for driver D. */
static void
-full_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+full_dim (struct tab_rendering *r, void *aux_)
{
- int i = 0;
- int columns = 5;
+ const struct outp_driver *d = r->driver;
+ const struct tab_table *t = r->table;
+ const struct full_dim_aux *aux = aux_;
+ int i;
- if (cmd.labels == FRQ_LABELS)
+ for (i = 0; i < tab_nc (t); i++)
{
- t->w[0] = MIN (tab_natural_width (t, d, 0), d->prop_em_width * 15);
- i = 1;
- columns ++;
+ r->w[i] = tab_natural_width (r, i);
+ if (aux->show_labels && i == 0)
+ r->w[i] = MIN (r->w[i], d->prop_em_width * 15);
+ else
+ r->w[i] = MAX (r->w[i], d->prop_em_width * 8);
}
- for (;i < columns; i++)
- t->w[i] = MAX (tab_natural_width (t, d, i), d->prop_em_width * 8);
+ for (i = 0; i < tab_nr (t); i++)
+ r->h[i] = d->font_height;
+}
- for (i = 0; i < t->nr; i++)
- t->h[i] = d->font_height;
+static void
+full_dim_free (void *aux_)
+{
+ struct full_dim_aux *aux = aux_;
+ free (aux);
}
/* Displays a full frequency table for variable V. */
const bool lab = (cmd.labels == FRQ_LABELS);
+ struct full_dim_aux *aux;
+
vf = get_var_freqs (v);
ft = &vf->tab;
n_categories = ft->n_valid + ft->n_missing;
- t = tab_create (5 + lab, n_categories + 2, 0);
+ t = tab_create (5 + lab, n_categories + 2);
tab_headers (t, 0, 0, 1, 0);
- tab_dim (t, full_dim, NULL);
+
+ aux = xmalloc (sizeof *aux);
+ aux->show_labels = lab;
+ tab_dim (t, full_dim, full_dim_free, aux);
if (lab)
tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Value Label"));
/* 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. */
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"));
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++)
0, 0, 3, r - 1);
tab_hline (t, TAL_2, 0, 3, 2);
tab_title (t, "%s", var_to_string (v));
- tab_columns (t, SOM_COL_DOWN, 1);
+ tab_columns (t, SOM_COL_DOWN);
tab_submit (t);
}
\f
}
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) ;
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
double x_min = DBL_MAX;
double x_max = -DBL_MAX;
- struct statistic *hist;
+ struct histogram *hist;
const double bins = 11;
struct hsh_iterator hi;
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;
}
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;
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);
}
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);
#include <language/stats/chisquare.h>
#include <language/stats/wilcoxon.h>
#include <language/stats/sign.h>
+#include <libpspp/cast.h>
#include <libpspp/hash.h>
#include <libpspp/pool.h>
#include <libpspp/taint.h>
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,
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;
}
{
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;
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;
}
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,
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) )
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;
}
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),
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;
}
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) )
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;
}
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]);
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,
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 */
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,
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,
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,
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);
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);
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);
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);
{
- 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));
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"));
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"));
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"));
#include <config.h>
+#include <language/stats/roc.h>
+
#include <data/procedure.h>
#include <language/lexer/variable-parser.h>
#include <language/lexer/value-parser.h>
#include <gsl/gsl_cdf.h>
#include <output/table.h>
-#include <output/charts/plot-chart.h>
-#include <output/charts/cartesian.h>
+#include <output/chart.h>
+#include <output/charts/roc-chart.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
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
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);
return process_group (var, reader, gt, dict, &rs->n1,
&rs->cutpoint_rdr,
ge,
- TP, FN);
+ ROC_TP, ROC_FN);
}
/*
return process_group (var, reader, lt, dict, &rs->n2,
&rs->cutpoint_rdr,
lt,
- TN, FP);
+ ROC_TN, ROC_FP);
}
{
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);
}
/*
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
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)
{
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"));
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"));
{
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,
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"));
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);
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);
}
-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);
}
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef LANGUAGE_STATS_ROC_H
+#define LANGUAGE_STATS_ROC_H 1
+
+/* These are case indexes into the cutpoint case readers for ROC
+ output, used by roc.c and roc-chart.c. */
+#define ROC_CUTPOINT 0
+#define ROC_TP 1
+#define ROC_FN 2
+#define ROC_TN 3
+#define ROC_FP 4
+
+#endif /* language/stats/roc.h */
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"));
/* 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"));
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"));
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,
ssbox_base_init (struct ssbox *this, int cols, int rows)
{
this->finalize = ssbox_base_finalize;
- this->t = tab_create (cols, rows, 0);
+ this->t = tab_create (cols, rows);
- tab_columns (this->t, SOM_COL_DOWN, 1);
+ tab_columns (this->t, SOM_COL_DOWN);
tab_headers (this->t, 0, 0, 1, 0);
tab_box (this->t, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols - 1, rows - 1);
tab_hline (this->t, TAL_2, 0, cols- 1, 1);
- tab_dim (this->t, tab_natural_dimensions, NULL);
+ tab_dim (this->t, tab_natural_dimensions, NULL, NULL);
}
\f
/* ssbox implementations. */
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 */
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 */
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"));
/* 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"));
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));
}
- 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);
)
{
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"));
/* 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"));
}
}
- 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);
#include <language/command.h>
#include <language/lexer/lexer.h>
#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
#include <libpspp/message.h>
#include <libpspp/message.h>
#include <libpspp/str.h>
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;
}
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)
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));
ss->parent.name = name ;
ss->parent.location = line_number;
- return (struct getl_interface *) ss;
+ return &ss->parent;
}
/* 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
#include <config.h>
+#include <libpspp/cast.h>
#include <libpspp/getl.h>
#include <libpspp/compiler.h>
#include <libpspp/str.h>
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);
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;
sss->parent.location = location;
- return (struct getl_interface *) sss;
+ return &sss->parent;
}
/* Return the syntax currently contained in S.
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)));
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;
}
/* 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
#endif
#include <libpspp/abt.h>
+#include <libpspp/cast.h>
#include <stdbool.h>
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);
}
{
cmp = abt->compare (target, p, abt->aux);
if (cmp == 0)
- return (struct abt_node *) p;
+ return CONST_CAST (struct abt_node *, p);
}
return NULL;
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);
}
}
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);
}
}
/* PSPP - a program for statistical analysis.
- Copyright (C) 2007 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
tree paper. */
#include <stddef.h>
+#include <libpspp/cast.h>
/* Returns the data structure corresponding to the given NODE,
assuming that NODE is embedded as the given MEMBER name in
data type STRUCT. */
-#define abt_data(NODE, STRUCT, MEMBER) \
- ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+#define abt_data(NODE, STRUCT, MEMBER) \
+ (CHECK_POINTER_HAS_TYPE (NODE, struct abt_node *), \
+ UP_CAST (NODE, STRUCT, MEMBER))
/* Node in an augmented binary tree. */
struct abt_node
/* PSPP - a program for statistical analysis.
- Copyright (C) 2007 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <stdbool.h>
#include <stdint.h>
+#include <libpspp/cast.h>
+
static void rebalance_subtree (struct bt *, struct bt_node *, size_t);
static struct bt_node **down_link (struct bt *, struct bt_node *);
{
cmp = bt->compare (target, p, bt->aux);
if (cmp == 0)
- return (struct bt_node *) p;
+ return CONST_CAST (struct bt_node *, p);
}
return NULL;
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
break;
}
}
- return (struct bt_node *) q;
+ return CONST_CAST (struct bt_node *, q);
}
/* Returns the node in BT following P in in-order.
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);
}
}
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);
}
}
/* PSPP - a program for statistical analysis.
- Copyright (C) 2007 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
fully encapsulated. */
#include <stddef.h>
+#include <libpspp/cast.h>
/* Returns the data structure corresponding to the given NODE,
assuming that NODE is embedded as the given MEMBER name in
data type STRUCT. */
-#define bt_data(NODE, STRUCT, MEMBER) \
- ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+#define bt_data(NODE, STRUCT, MEMBER) \
+ (CHECK_POINTER_HAS_TYPE (NODE, struct bt_node *), \
+ UP_CAST (NODE, STRUCT, MEMBER))
/* Node in a balanced binary tree. */
struct bt_node
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef LIBPSPP_CAST_H
+#define LIBPSPP_CAST_H 1
+
+#include <stddef.h>
+
+/* Expands to a void expression that checks that POINTER is an
+ expression whose type is a qualified or unqualified version of
+ a type compatible with TYPE (a pointer type) and, if not,
+ causes a compiler warning to be issued (on typical compilers).
+
+ Examples:
+
+ int *ip;
+ const int *cip;
+ const int **cipp;
+ int ***ippp;
+ double *dp;
+
+ // None of these causes a warning:
+ CHECK_POINTER_HAS_TYPE (ip, int *);
+ CHECK_POINTER_HAS_TYPE (ip, const int *);
+ CHECK_POINTER_HAS_TYPE (cip, int *);
+ CHECK_POINTER_HAS_TYPE (cip, const int *);
+ CHECK_POINTER_HAS_TYPE (dp, double *);
+ CHECK_POINTER_HAS_TYPE (dp, const double *);
+ CHECK_POINTER_HAS_TYPE (cipp, const int **);
+ CHECK_POINTER_HAS_TYPE (cipp, const int *const *);
+ CHECK_POINTER_HAS_TYPE (ippp, int ***);
+ CHECK_POINTER_HAS_TYPE (ippp, int **const *);
+
+ // None of these causes a warning either, although it is unusual to
+ // const-qualify a pointer like this (it's like declaring a "const int",
+ // for example).
+ CHECK_POINTER_HAS_TYPE (ip, int *const);
+ CHECK_POINTER_HAS_TYPE (ip, const int *const);
+ CHECK_POINTER_HAS_TYPE (cip, int *const);
+ CHECK_POINTER_HAS_TYPE (cip, const int *const);
+ CHECK_POINTER_HAS_TYPE (cipp, const int **const);
+ CHECK_POINTER_HAS_TYPE (cipp, const int *const *const);
+ CHECK_POINTER_HAS_TYPE (ippp, int ***const);
+ CHECK_POINTER_HAS_TYPE (ippp, int **const *const);
+
+ // Provokes a warning because "int" is not compatible with "double":
+ CHECK_POINTER_HAS_TYPE (dp, int *);
+
+ // Provoke warnings because C's type compatibility rules only allow
+ // adding a "const" qualifier to the outermost pointer:
+ CHECK_POINTER_HAS_TYPE (ippp, const int ***);
+ CHECK_POINTER_HAS_TYPE (ippp, int *const**);
+*/
+#define CHECK_POINTER_HAS_TYPE(POINTER, TYPE) \
+ ((void) sizeof ((TYPE) (POINTER) == (POINTER)))
+
+/* Given expressions A and B, both of which have pointer type,
+ expands to a void expression that causes a compiler warning if
+ A and B are not pointers to qualified or unqualified versions
+ of compatible types.
+
+ Examples similar to those given for CHECK_POINTER_HAS_TYPE,
+ above, can easily be devised. */
+#define CHECK_POINTER_COMPATIBILITY(A, B) ((void) sizeof ((A) == (B)))
+
+/* Equivalent to casting POINTER to TYPE, but also issues a
+ warning if the cast changes anything other than an outermost
+ "const" or "volatile" qualifier. */
+#define CONST_CAST(TYPE, POINTER) \
+ (CHECK_POINTER_HAS_TYPE (POINTER, TYPE), \
+ (TYPE) (POINTER))
+
+/* Given POINTER, a pointer to the given MEMBER within structure
+ STRUCT, returns the address of the STRUCT. */
+#define UP_CAST(POINTER, STRUCT, MEMBER) \
+ (CHECK_POINTER_COMPATIBILITY (&((STRUCT *) 0)->MEMBER, POINTER), \
+ (STRUCT *) ((char *) (POINTER) - offsetof (STRUCT, MEMBER)))
+
+#endif /* libpspp/cast.h */
/* 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
/* 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
#ifndef LIBPSPP_HEAP_H
#define LIBPSPP_HEAP_H 1
+#include <libpspp/cast.h>
#include <stdbool.h>
#include <stddef.h>
/* 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. */
/* PSPP - a program for statistical analysis.
- Copyright (C) 2008 Free Software Foundation, Inc.
+ Copyright (C) 2008, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
*/
#include <stddef.h>
+#include <libpspp/cast.h>
/* Returns the data structure corresponding to the given NODE,
assuming that NODE is embedded as the given MEMBER name in
data type STRUCT. NODE must not be a null pointer. */
#define HMAP_DATA(NODE, STRUCT, MEMBER) \
- ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+ (CHECK_POINTER_HAS_TYPE (NODE, struct hmap_node *), \
+ UP_CAST (NODE, STRUCT, MEMBER))
/* Like HMAP_DATA, except that a null NODE yields a null pointer
result. */
/* 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
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
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
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.
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
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.
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
if (predicate (x, aux))
return NULL;
- return (struct ll *) partition;
+ return CONST_CAST (struct ll *, partition);
}
\f
/* 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
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
+#include <libpspp/cast.h>
+
+#include <libpspp/cast.h>
/* Embedded, circular doubly linked list.
/* Returns the data structure corresponding to the given node LL,
assuming that LL is embedded as the given MEMBER name in data
type STRUCT. */
-#define ll_data(LL, STRUCT, MEMBER) \
- ((STRUCT *) ((char *) (LL) - offsetof (STRUCT, MEMBER)))
+#define ll_data(LL, STRUCT, MEMBER) \
+ (CHECK_POINTER_HAS_TYPE(LL, struct ll *), \
+ UP_CAST(LL, STRUCT, MEMBER))
/* Linked list node. */
struct ll
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,
/* 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
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
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
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.
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
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.
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
if (predicate (llx_data (x), aux))
return NULL;
- return (struct llx *) partition;
+ return CONST_CAST (struct llx *, partition);
}
\f
/* Allocates and returns a node using malloc. */
/* PSPP - a program for statistical analysis.
- Copyright (C) 2007 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <stdbool.h>
#include <libpspp/bt.h>
+#include <libpspp/cast.h>
/* Returns the data structure corresponding to the given NODE,
assuming that NODE is embedded as the given MEMBER name in
data type STRUCT. */
#define range_map_data(NODE, STRUCT, MEMBER) \
- ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+ (CHECK_POINTER_HAS_TYPE (NODE, struct range_map_node *), \
+ UP_CAST (NODE, STRUCT, MEMBER))
/* A range map node, to be embedded in the data value. */
struct range_map_node
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
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;
#include <stdbool.h>
#include <libpspp/bt.h>
+#include <libpspp/cast.h>
/* A set of ranges. */
struct range_set
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));
}
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));
}
#include <string.h>
#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
#include <libpspp/misc.h>
#include <libpspp/pool.h>
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;
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)
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)
#include <stdint.h>
#include <stdlib.h>
+#include <libpspp/cast.h>
#include <libpspp/message.h>
#include <libpspp/pool.h>
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';
/* PSPP - a program for statistical analysis.
- Copyright (C) 2007 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <libpspp/array.h>
#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
#include "xalloc.h"
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++;
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)
{
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);
}
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)
{
#include <stdlib.h>
#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
#include "error.h"
#include "xalloc.h"
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))
{
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)
/* PSPP - a program for statistical analysis.
- Copyright (C) 2007 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <limits.h>
#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
#include <libpspp/compiler.h>
static struct tower_node *abt_to_tower_node (const struct abt_node *);
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));
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));
/* PSPP - a program for statistical analysis.
- Copyright (C) 2007 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <stdbool.h>
#include <libpspp/abt.h>
+#include <libpspp/cast.h>
/* Returns the data structure corresponding to the given NODE,
assuming that NODE is embedded as the given MEMBER name in
data type STRUCT. */
-#define tower_data(NODE, STRUCT, MEMBER) \
- ((STRUCT *) ((char *) (NODE) - offsetof (STRUCT, MEMBER)))
+#define tower_data(NODE, STRUCT, MEMBER) \
+ (CHECK_POINTER_HAS_TYPE (NODE, struct tower_node *), \
+ UP_CAST (NODE, STRUCT, MEMBER))
/* A node within a tower. */
struct tower_node
/* PSPP - a program for statistical analysis.
- Copyright (C) 2008 Free Software Foundation, Inc.
+ Copyright (C) 2008, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include "tukey-hinges.h"
#include <gl/xalloc.h>
#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
#include <math.h>
#include <float.h>
#include <data/val-type.h>
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); )
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;
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;
ll_init (&w->outliers);
- return stat;
+ return w;
}
/* 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
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]);
/* 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
#include <gl/xalloc.h>
#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
#include <gsl/gsl_histogram.h>
#include "chart-geometry.h"
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);
}
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);
}
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);
stat->accumulate = acc;
stat->destroy = destroy;
- return stat;
+ return h;
}
/* 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
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);
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 ;
}
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 );
}
#include <data/case.h>
#include <data/casewriter.h>
#include <libpspp/compiler.h>
+#include <libpspp/cast.h>
#include <libpspp/misc.h>
#include <math/moments.h>
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);
}
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 ));
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;
stat->destroy = destroy;
stat->accumulate = acc;
- return os;
+ return np;
}
/* 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
};
-struct order_stats * np_create (const struct moments1 *);
+struct np * np_create (const struct moments1 *);
#endif
{
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];
/* 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
#define N_(msgid) msgid
#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
#include <data/val-type.h>
#include <gl/xalloc.h>
#include <data/variable.h>
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);
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);
stat->destroy = destroy;
- return os;
+ return ptl;
}
/* 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
/* 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);
/* PSPP - a program for statistical analysis.
- Copyright (C) 2008 Free Software Foundation, Inc.
+ Copyright (C) 2008, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <gl/xalloc.h>
#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
#include <math.h>
#include <data/val-type.h>
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;
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);
tm->w = W;
tm->tail = tail;
- return stat;
+ return tm;
}
/* 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
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
/* PSPP - a program for statistical analysis.
- Copyright (C) 2008 Free Software Foundation, Inc.
+ Copyright (C) 2008, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <gl/xalloc.h>
#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
#include <math.h>
void
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);
stat->destroy = destroy;
- return stat;
+ return th;
}
/* 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
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]);
/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <libpspp/pool.h>
#include <libpspp/start-date.h>
#include <libpspp/version.h>
+#include <output/chart-provider.h>
+#include <output/chart.h>
+#include <output/output.h>
-#include "chart.h"
#include "error.h"
#include "minmax.h"
-#include "output.h"
#include "xalloc.h"
#include "gettext.h"
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.
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? */
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;
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;
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))
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;
}
};
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;
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);
}
}
-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,
}
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. */
}
/* 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);
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",
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
};
## 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
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/cairo.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/start-date.h>
+#include <libpspp/version.h>
+#include <output/afm.h>
+#include <output/chart-provider.h>
+#include <output/manager.h>
+#include <output/output.h>
+
+#include <cairo/cairo-pdf.h>
+#include <cairo/cairo-ps.h>
+#include <cairo/cairo-svg.h>
+#include <cairo/cairo.h>
+#include <pango/pango-font.h>
+#include <pango/pango-layout.h>
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
+#include <stdlib.h>
+
+#include "error.h"
+#include "intprops.h"
+#include "minmax.h"
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* Cairo driver options: (defaults listed first)
+
+ output-file="pspp.pdf"
+ output-type=pdf|ps|png|svg
+ paper-size=letter (see "papersize" file)
+ orientation=portrait|landscape
+ headers=on|off
+
+ left-margin=0.5in
+ right-margin=0.5in
+ top-margin=0.5in
+ bottom-margin=0.5in
+
+ prop-font=serif
+ emph-font=serif italic
+ fixed-font=monospace
+ font-size=10000
+
+ line-gutter=1pt
+ line-spacing=1pt
+ line-width=0.5pt
+ */
+
+/* Measurements as we present to the rest of PSPP. */
+#define XR_POINT PANGO_SCALE
+#define XR_INCH (XR_POINT * 72)
+
+/* Conversions to and from points. */
+static double
+xr_to_pt (int x)
+{
+ return x / (double) XR_POINT;
+}
+
+static int
+pt_to_xr (double x)
+{
+ return x * XR_POINT + 0.5;
+}
+
+/* Output types. */
+enum xr_output_type
+ {
+ XR_PDF,
+ XR_PS,
+ XR_SVG
+ };
+
+/* A font for use with Cairo. */
+struct xr_font
+ {
+ char *string;
+ PangoFontDescription *desc;
+ PangoLayout *layout;
+ PangoFontMetrics *metrics;
+ };
+
+/* Cairo output driver extension record. */
+struct xr_driver_ext
+ {
+ cairo_t *cairo;
+ struct xr_font fonts[OUTP_FONT_CNT];
+
+ bool draw_headers; /* Draw headers at top of page? */
+ int page_number; /* Current page number. */
+
+ int line_gutter; /* Space around lines. */
+ int line_space; /* Space between lines. */
+ int line_width; /* Width of lines. */
+ };
+
+struct xr_driver_options
+ {
+ struct outp_driver *driver;
+
+ char *file_name; /* Output file name. */
+ enum xr_output_type file_type; /* Type of output file. */
+
+
+ bool portrait; /* Portrait mode? */
+
+ int paper_width; /* Width of paper before dropping margins. */
+ int paper_length; /* Length of paper before dropping margins. */
+ int left_margin; /* Left margin in XR units. */
+ int right_margin; /* Right margin in XR units. */
+ int top_margin; /* Top margin in XR units. */
+ int bottom_margin; /* Bottom margin in XR units. */
+ };
+
+static bool handle_option (void *options, const char *key,
+ const struct string *val);
+static void draw_headers (struct outp_driver *this);
+
+static bool load_font (struct outp_driver *this, struct xr_font *);
+static void free_font (struct xr_font *);
+static int text_width (struct outp_driver *, const char *, enum outp_font);
+\f
+/* Driver initialization. */
+
+static struct outp_driver *
+xr_allocate (const char *name, int types)
+{
+ struct outp_driver *this;
+ struct xr_driver_ext *x;
+ size_t i;
+
+ this = outp_allocate_driver (&cairo_class, name, types);
+ this->width = this->length = 0;
+ this->font_height = XR_POINT * 10;
+ this->ext = x = xzalloc (sizeof *x);
+ x->cairo = NULL;
+ x->fonts[OUTP_FIXED].string = xstrdup ("monospace");
+ x->fonts[OUTP_PROPORTIONAL].string = xstrdup ("serif");
+ x->fonts[OUTP_EMPHASIS].string = xstrdup ("serif italic");
+ for (i = 0; i < OUTP_FONT_CNT; i++)
+ {
+ struct xr_font *font = &x->fonts[i];
+ font->desc = NULL;
+ font->metrics = NULL;
+ font->layout = NULL;
+ }
+ x->draw_headers = true;
+ x->page_number = 0;
+ x->line_gutter = XR_POINT;
+ x->line_space = XR_POINT;
+ x->line_width = XR_POINT / 2;
+
+ return this;
+}
+
+static bool
+xr_set_cairo (struct outp_driver *this, cairo_t *cairo)
+{
+ struct xr_driver_ext *x = this->ext;
+ int i;
+
+ x->cairo = cairo;
+
+ cairo_set_line_width (x->cairo, xr_to_pt (x->line_width));
+
+ for (i = 0; i < OUTP_FONT_CNT; i++)
+ if (!load_font (this, &x->fonts[i]))
+ return false;
+
+ this->fixed_width = text_width (this, "0", OUTP_FIXED);
+ this->prop_em_width = text_width (this, "0", OUTP_PROPORTIONAL);
+
+ this->horiz_line_width[OUTP_L_NONE] = 0;
+ this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
+ this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
+ + 2 * x->line_width);
+ memcpy (this->vert_line_width, this->horiz_line_width,
+ sizeof this->vert_line_width);
+
+ return true;
+}
+
+struct outp_driver *
+xr_create_driver (cairo_t *cairo)
+{
+ struct outp_driver *this;
+
+ this = xr_allocate ("cairo", 0);
+ this->width = INT_MAX / 8;
+ this->length = INT_MAX / 8;
+ if (!xr_set_cairo (this, cairo))
+ {
+ this->class->close_driver (this);
+ outp_free_driver (this);
+ return NULL;
+ }
+ return this;
+}
+
+static bool
+xr_open_driver (const char *name, int types, struct substring option_string)
+{
+ struct outp_driver *this;
+ struct xr_driver_ext *x;
+ struct xr_driver_options options;
+ cairo_surface_t *surface;
+ cairo_status_t status;
+ double width_pt, length_pt;
+
+ this = xr_allocate (name, types);
+ x = this->ext;
+
+ options.driver = this;
+ options.file_name = xstrdup ("pspp.pdf");
+ options.file_type = XR_PDF;
+ options.portrait = true;
+ outp_get_paper_size ("", &options.paper_width, &options.paper_length);
+ options.left_margin = XR_INCH / 2;
+ options.right_margin = XR_INCH / 2;
+ options.top_margin = XR_INCH / 2;
+ options.bottom_margin = XR_INCH / 2;
+
+ outp_parse_options (this->name, option_string, handle_option, &options);
+
+ width_pt = options.paper_width / 1000.0;
+ length_pt = options.paper_length / 1000.0;
+ if (options.portrait)
+ {
+ this->width = pt_to_xr (width_pt);
+ this->length = pt_to_xr (length_pt);
+ }
+ else
+ {
+ this->width = pt_to_xr (width_pt);
+ this->length = pt_to_xr (length_pt);
+ }
+ if (x->draw_headers)
+ options.top_margin += 3 * this->font_height;
+ this->width -= options.left_margin + options.right_margin;
+ this->length -= options.top_margin + options.bottom_margin;
+
+ if (options.file_type == XR_PDF)
+ surface = cairo_pdf_surface_create (options.file_name,
+ width_pt, length_pt);
+ else if (options.file_type == XR_PS)
+ surface = cairo_ps_surface_create (options.file_name, width_pt, length_pt);
+ else if (options.file_type == XR_SVG)
+ surface = cairo_svg_surface_create (options.file_name,
+ width_pt, length_pt);
+ else
+ NOT_REACHED ();
+
+ status = cairo_surface_status (surface);
+ if (status != CAIRO_STATUS_SUCCESS)
+ {
+ error (0, 0, _("opening output file \"%s\": %s"),
+ options.file_name, cairo_status_to_string (status));
+ cairo_surface_destroy (surface);
+ goto error;
+ }
+
+ x->cairo = cairo_create (surface);
+ cairo_surface_destroy (surface);
+
+ cairo_translate (x->cairo,
+ xr_to_pt (options.left_margin),
+ xr_to_pt (options.top_margin));
+
+ if (this->length / this->font_height < 15)
+ {
+ error (0, 0, _("The defined page is not long "
+ "enough to hold margins and headers, plus least 15 "
+ "lines of the default fonts. In fact, there's only "
+ "room for %d lines."),
+ this->length / this->font_height);
+ goto error;
+ }
+
+ if (!xr_set_cairo (this, x->cairo))
+ goto error;
+
+ outp_register_driver (this);
+ free (options.file_name);
+ return true;
+
+ error:
+ this->class->close_driver (this);
+ outp_free_driver (this);
+ free (options.file_name);
+ return false;
+}
+
+static bool
+xr_close_driver (struct outp_driver *this)
+{
+ struct xr_driver_ext *x = this->ext;
+ bool ok = true;
+ size_t i;
+
+ if (x->cairo != NULL)
+ {
+ cairo_status_t status;
+
+ cairo_surface_finish (cairo_get_target (x->cairo));
+ status = cairo_status (x->cairo);
+ if (status != CAIRO_STATUS_SUCCESS)
+ error (0, 0, _("error writing output file for %s driver: %s"),
+ this->name, cairo_status_to_string (status));
+ cairo_destroy (x->cairo);
+ }
+
+ for (i = 0; i < OUTP_FONT_CNT; i++)
+ free_font (&x->fonts[i]);
+ free (x);
+
+ return ok;
+}
+
+/* Generic option types. */
+enum
+{
+ output_file_arg,
+ output_type_arg,
+ paper_size_arg,
+ orientation_arg,
+ line_style_arg,
+ boolean_arg,
+ dimension_arg,
+ string_arg,
+ nonneg_int_arg
+};
+
+/* All the options that the Cairo driver supports. */
+static const struct outp_option option_tab[] =
+{
+ {"output-file", output_file_arg,0},
+ {"output-type", output_type_arg,0},
+ {"paper-size", paper_size_arg, 0},
+ {"orientation", orientation_arg,0},
+
+ {"headers", boolean_arg, 1},
+
+ {"prop-font", string_arg, OUTP_PROPORTIONAL},
+ {"emph-font", string_arg, OUTP_EMPHASIS},
+ {"fixed-font", string_arg, OUTP_FIXED},
+
+ {"left-margin", dimension_arg, 0},
+ {"right-margin", dimension_arg, 1},
+ {"top-margin", dimension_arg, 2},
+ {"bottom-margin", dimension_arg, 3},
+ {"font-size", dimension_arg, 4},
+ {"line-width", dimension_arg, 5},
+ {"line-gutter", dimension_arg, 6},
+ {"line-width", dimension_arg, 7},
+ {NULL, 0, 0},
+};
+
+static bool
+handle_option (void *options_, const char *key, const struct string *val)
+{
+ struct xr_driver_options *options = options_;
+ struct outp_driver *this = options->driver;
+ struct xr_driver_ext *x = this->ext;
+ int subcat;
+ char *value = ds_cstr (val);
+
+ switch (outp_match_keyword (key, option_tab, &subcat))
+ {
+ case -1:
+ error (0, 0,
+ _("unknown configuration parameter `%s' for %s device "
+ "driver"), key, this->class->name);
+ break;
+ case output_file_arg:
+ free (options->file_name);
+ options->file_name = xstrdup (value);
+ break;
+ case output_type_arg:
+ if (!strcmp (value, "pdf"))
+ options->file_type = XR_PDF;
+ else if (!strcmp (value, "ps"))
+ options->file_type = XR_PS;
+ else if (!strcmp (value, "svg"))
+ options->file_type = XR_SVG;
+ else
+ {
+ error (0, 0, _("unknown Cairo output type \"%s\""), value);
+ return false;
+ }
+ break;
+ case paper_size_arg:
+ outp_get_paper_size (value,
+ &options->paper_width, &options->paper_length);
+ break;
+ case orientation_arg:
+ if (!strcmp (value, "portrait"))
+ options->portrait = true;
+ else if (!strcmp (value, "landscape"))
+ options->portrait = false;
+ else
+ error (0, 0, _("unknown orientation `%s' (valid orientations are "
+ "`portrait' and `landscape')"), value);
+ break;
+ case boolean_arg:
+ if (!strcmp (value, "on") || !strcmp (value, "true")
+ || !strcmp (value, "yes") || atoi (value))
+ x->draw_headers = true;
+ else if (!strcmp (value, "off") || !strcmp (value, "false")
+ || !strcmp (value, "no") || !strcmp (value, "0"))
+ x->draw_headers = false;
+ else
+ {
+ error (0, 0, _("boolean value expected for %s"), key);
+ return false;
+ }
+ break;
+ case dimension_arg:
+ {
+ int dimension = outp_evaluate_dimension (value);
+
+ if (dimension <= 0)
+ break;
+ switch (subcat)
+ {
+ case 0:
+ options->left_margin = dimension;
+ break;
+ case 1:
+ options->right_margin = dimension;
+ break;
+ case 2:
+ options->top_margin = dimension;
+ break;
+ case 3:
+ options->bottom_margin = dimension;
+ break;
+ case 4:
+ this->font_height = dimension;
+ break;
+ case 5:
+ x->line_width = dimension;
+ break;
+ case 6:
+ x->line_gutter = dimension;
+ break;
+ case 7:
+ x->line_width = dimension;
+ break;
+ default:
+ NOT_REACHED ();
+ }
+ }
+ break;
+ case string_arg:
+ free (x->fonts[subcat].string);
+ x->fonts[subcat].string = ds_xstrdup (val);
+ break;
+ default:
+ NOT_REACHED ();
+ }
+
+ return true;
+}
+\f
+/* Basic file operations. */
+
+static void
+xr_open_page (struct outp_driver *this)
+{
+ struct xr_driver_ext *x = this->ext;
+
+ x->page_number++;
+
+ if (x->draw_headers)
+ draw_headers (this);
+}
+
+static void
+xr_close_page (struct outp_driver *this)
+{
+ struct xr_driver_ext *x = this->ext;
+ cairo_show_page (x->cairo);
+}
+
+static void
+xr_output_chart (struct outp_driver *this, const struct chart *chart)
+{
+ struct xr_driver_ext *x = this->ext;
+ struct chart_geometry geom;
+
+ outp_eject_page (this);
+ outp_open_page (this);
+
+ cairo_save (x->cairo);
+ cairo_translate (x->cairo, 0.0, xr_to_pt (this->length));
+ cairo_scale (x->cairo, 1.0, -1.0);
+ chart_geometry_init (x->cairo, &geom,
+ xr_to_pt (this->width), xr_to_pt (this->length));
+ chart_draw (chart, x->cairo, &geom);
+ chart_geometry_free (x->cairo, &geom);
+ cairo_restore (x->cairo);
+
+ outp_close_page (this);
+}
+\f
+/* Draws a line from (x0,y0) to (x1,y1). */
+static void
+dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
+{
+ struct xr_driver_ext *x = this->ext;
+ cairo_new_path (x->cairo);
+ cairo_move_to (x->cairo, xr_to_pt (x0), xr_to_pt (y0));
+ cairo_line_to (x->cairo, xr_to_pt (x1), xr_to_pt (y1));
+ cairo_stroke (x->cairo);
+}
+
+/* Draws a horizontal line X0...X2 at Y if LEFT says so,
+ shortening it to X0...X1 if SHORTEN is true.
+ Draws a horizontal line X1...X3 at Y if RIGHT says so,
+ shortening it to X2...X3 if SHORTEN is true. */
+static void
+horz_line (struct outp_driver *this,
+ int x0, int x1, int x2, int x3, int y,
+ enum outp_line_style left, enum outp_line_style right,
+ bool shorten)
+{
+ if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
+ dump_line (this, x0, y, x3, y);
+ else
+ {
+ if (left != OUTP_L_NONE)
+ dump_line (this, x0, y, shorten ? x1 : x2, y);
+ if (right != OUTP_L_NONE)
+ dump_line (this, shorten ? x2 : x1, y, x3, y);
+ }
+}
+
+/* Draws a vertical line Y0...Y2 at X if TOP says so,
+ shortening it to Y0...Y1 if SHORTEN is true.
+ Draws a vertical line Y1...Y3 at X if BOTTOM says so,
+ shortening it to Y2...Y3 if SHORTEN is true. */
+static void
+vert_line (struct outp_driver *this,
+ int y0, int y1, int y2, int y3, int x,
+ enum outp_line_style top, enum outp_line_style bottom,
+ bool shorten)
+{
+ if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
+ dump_line (this, x, y0, x, y3);
+ else
+ {
+ if (top != OUTP_L_NONE)
+ dump_line (this, x, y0, x, shorten ? y1 : y2);
+ if (bottom != OUTP_L_NONE)
+ dump_line (this, x, shorten ? y2 : y1, x, y3);
+ }
+}
+
+/* Draws a generalized intersection of lines in the rectangle
+ (X0,Y0)-(X3,Y3). The line coming from the top to the center
+ is of style TOP, from left to center of style LEFT, from
+ bottom to center of style BOTTOM, and from right to center of
+ style RIGHT. */
+static void
+xr_line (struct outp_driver *this,
+ int x0, int y0, int x3, int y3,
+ enum outp_line_style top, enum outp_line_style left,
+ enum outp_line_style bottom, enum outp_line_style right)
+{
+ /* The algorithm here is somewhat subtle, to allow it to handle
+ all the kinds of intersections that we need.
+
+ Three additional ordinates are assigned along the x axis. The
+ first is xc, midway between x0 and x3. The others are x1 and
+ x2; for a single vertical line these are equal to xc, and for
+ a double vertical line they are the ordinates of the left and
+ right half of the double line.
+
+ yc, y1, and y2 are assigned similarly along the y axis.
+
+ The following diagram shows the coordinate system and output
+ for double top and bottom lines, single left line, and no
+ right line:
+
+ x0 x1 xc x2 x3
+ y0 ________________________
+ | # # |
+ | # # |
+ | # # |
+ | # # |
+ | # # |
+ y1 = y2 = yc |######### # |
+ | # # |
+ | # # |
+ | # # |
+ | # # |
+ y3 |________#_____#_______|
+ */
+ struct xr_driver_ext *ext = this->ext;
+
+ /* Offset from center of each line in a pair of double lines. */
+ int double_line_ofs = (ext->line_space + ext->line_width) / 2;
+
+ /* Are the lines along each axis single or double?
+ (It doesn't make sense to have different kinds of line on the
+ same axis, so we don't try to gracefully handle that case.) */
+ bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
+ bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
+
+ /* When horizontal lines are doubled,
+ the left-side line along y1 normally runs from x0 to x2,
+ and the right-side line along y1 from x3 to x1.
+ If the top-side line is also doubled, we shorten the y1 lines,
+ so that the left-side line runs only to x1,
+ and the right-side line only to x2.
+ Otherwise, the horizontal line at y = y1 below would cut off
+ the intersection, which looks ugly:
+ x0 x1 x2 x3
+ y0 ________________________
+ | # # |
+ | # # |
+ | # # |
+ | # # |
+ y1 |######### ########|
+ | |
+ | |
+ y2 |######################|
+ | |
+ | |
+ y3 |______________________|
+ It is more of a judgment call when the horizontal line is
+ single. We actually choose to cut off the line anyhow, as
+ shown in the first diagram above.
+ */
+ bool shorten_y1_lines = top == OUTP_L_DOUBLE;
+ bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
+ bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
+ int horz_line_ofs = double_vert ? double_line_ofs : 0;
+ int xc = (x0 + x3) / 2;
+ int x1 = xc - horz_line_ofs;
+ int x2 = xc + horz_line_ofs;
+
+ bool shorten_x1_lines = left == OUTP_L_DOUBLE;
+ bool shorten_x2_lines = right == OUTP_L_DOUBLE;
+ bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
+ int vert_line_ofs = double_horz ? double_line_ofs : 0;
+ int yc = (y0 + y3) / 2;
+ int y1 = yc - vert_line_ofs;
+ int y2 = yc + vert_line_ofs;
+
+ if (!double_horz)
+ horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
+ else
+ {
+ horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
+ horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
+ }
+
+ if (!double_vert)
+ vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
+ else
+ {
+ vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
+ vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
+ }
+}
+
+/* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
+ and with the given JUSTIFICATION for THIS driver. */
+static int
+draw_text (struct outp_driver *this,
+ const char *string, int x, int y, int max_width,
+ enum outp_justification justification)
+{
+ struct outp_text text;
+ int width;
+
+ text.font = OUTP_PROPORTIONAL;
+ text.justification = justification;
+ text.string = ss_cstr (string);
+ text.h = max_width;
+ text.v = this->font_height;
+ text.x = x;
+ text.y = y;
+ this->class->text_metrics (this, &text, &width, NULL);
+ this->class->text_draw (this, &text);
+ return width;
+}
+
+/* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
+ and with the given JUSTIFICATION for THIS driver. */
+static int
+text_width (struct outp_driver *this, const char *string, enum outp_font font)
+{
+ struct outp_text text;
+ int width;
+
+ text.font = font;
+ text.justification = OUTP_LEFT;
+ text.string = ss_cstr (string);
+ text.h = INT_MAX;
+ text.v = this->font_height;
+ text.x = 0;
+ text.y = 0;
+ this->class->text_metrics (this, &text, &width, NULL);
+ return width;
+}
+
+/* Writes LEFT left-justified and RIGHT right-justified within
+ (X0...X1) at Y. LEFT or RIGHT or both may be null. */
+static void
+draw_header_line (struct outp_driver *this,
+ const char *left, const char *right,
+ int x0, int x1, int y)
+{
+ int right_width = 0;
+ if (right != NULL)
+ right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
+ + this->prop_em_width);
+ if (left != NULL)
+ draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
+}
+
+/* Draw top of page headers for THIS driver. */
+static void
+draw_headers (struct outp_driver *this)
+{
+ struct xr_driver_ext *ext = this->ext;
+ char *r1, *r2;
+ int x0, x1;
+ int y;
+
+ y = -3 * this->font_height;
+ x0 = this->prop_em_width;
+ x1 = this->width - this->prop_em_width;
+
+ /* Draw box. */
+ cairo_rectangle (ext->cairo, 0, xr_to_pt (y), xr_to_pt (this->width),
+ xr_to_pt (2 * (this->font_height
+ + ext->line_width + ext->line_gutter)));
+ cairo_save (ext->cairo);
+ cairo_set_source_rgb (ext->cairo, 0.9, 0.9, 0.9);
+ cairo_fill_preserve (ext->cairo);
+ cairo_restore (ext->cairo);
+ cairo_stroke (ext->cairo);
+
+ y += ext->line_width + ext->line_gutter;
+
+ r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
+ r2 = xasprintf ("%s - %s", version, host_system);
+
+ draw_header_line (this, outp_title, r1, x0, x1, y);
+ y += this->font_height;
+
+ draw_header_line (this, outp_subtitle, r2, x0, x1, y);
+
+ free (r1);
+ free (r2);
+}
+\f
+/* Format TEXT on THIS driver.
+ If DRAW is nonzero, draw the text.
+ The width of the widest line is stored into *WIDTH, if WIDTH
+ is nonnull.
+ The total height of the text written is stored into *HEIGHT,
+ if HEIGHT is nonnull. */
+static void
+text (struct outp_driver *this, const struct outp_text *text, bool draw,
+ int *width, int *height)
+{
+ struct xr_driver_ext *ext = this->ext;
+ struct xr_font *font = &ext->fonts[text->font];
+
+ pango_layout_set_text (font->layout,
+ text->string.string, text->string.length);
+ pango_layout_set_alignment (
+ font->layout,
+ (text->justification == OUTP_RIGHT ? PANGO_ALIGN_RIGHT
+ : text->justification == OUTP_LEFT ? PANGO_ALIGN_LEFT
+ : PANGO_ALIGN_CENTER));
+ pango_layout_set_width (font->layout, text->h == INT_MAX ? -1 : text->h);
+ pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD_CHAR);
+ /* XXX need to limit number of lines to those that fit in text->v. */
+
+ if (draw)
+ {
+ int x = text->x;
+ if (text->justification != OUTP_LEFT && text->h != INT_MAX)
+ {
+ int w, h, excess;
+ pango_layout_get_size (font->layout, &w, &h);
+ excess = text->h - w;
+ if (excess > 0)
+ {
+ if (text->justification == OUTP_CENTER)
+ x += excess / 2;
+ else
+ x += excess;
+ }
+ }
+ cairo_save (ext->cairo);
+ cairo_translate (ext->cairo, xr_to_pt (text->x), xr_to_pt (text->y));
+ pango_cairo_show_layout (ext->cairo, font->layout);
+ cairo_restore (ext->cairo);
+ }
+
+ if (width != NULL || height != NULL)
+ {
+ int w, h;
+ pango_layout_get_size (font->layout, &w, &h);
+ if (width != NULL)
+ *width = w;
+ if (height != NULL)
+ *height = h;
+ }
+}
+
+static void
+xr_text_metrics (struct outp_driver *this, const struct outp_text *t,
+ int *width, int *height)
+{
+ text (this, t, false, width, height);
+}
+
+static void
+xr_text_draw (struct outp_driver *this, const struct outp_text *t)
+{
+ text (this, t, true, NULL, NULL);
+}
+\f
+/* Attempts to load FONT, initializing its other members based on
+ its 'string' member and the information in THIS. Returns true
+ if successful, otherwise false. */
+static bool
+load_font (struct outp_driver *this, struct xr_font *font)
+{
+ struct xr_driver_ext *x = this->ext;
+ PangoContext *context;
+ PangoLanguage *language;
+
+ font->desc = pango_font_description_from_string (font->string);
+ if (font->desc == NULL)
+ {
+ error (0, 0, _("\"%s\": bad font specification"), font->string);
+ return false;
+ }
+ pango_font_description_set_absolute_size (font->desc, this->font_height);
+
+ font->layout = pango_cairo_create_layout (x->cairo);
+ pango_layout_set_font_description (font->layout, font->desc);
+
+ language = pango_language_get_default ();
+ context = pango_layout_get_context (font->layout);
+ font->metrics = pango_context_get_metrics (context, font->desc, language);
+
+ return true;
+}
+
+/* Frees FONT. */
+static void
+free_font (struct xr_font *font)
+{
+ free (font->string);
+ if (font->desc != NULL)
+ pango_font_description_free (font->desc);
+ pango_font_metrics_unref (font->metrics);
+ g_object_unref (font->layout);
+}
+
+/* Cairo driver class. */
+const struct outp_class cairo_class =
+{
+ "cairo",
+ 0,
+
+ xr_open_driver,
+ xr_close_driver,
+
+ xr_open_page,
+ xr_close_page,
+ NULL,
+
+ xr_output_chart,
+
+ NULL,
+
+ xr_line,
+ xr_text_metrics,
+ xr_text_draw,
+};
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CAIRO_H
+#define OUTPUT_CAIRO_H 1
+
+#include <cairo/cairo.h>
+
+struct outp_driver *xr_create_driver (cairo_t *);
+
+#endif /* output/cairo.h */
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2004, 2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CHART_PROVIDER_H
+#define OUTPUT_CHART_PROVIDER_H 1
+
+#include <cairo/cairo.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <output/chart.h>
+
+struct chart_colour
+ {
+ uint8_t red;
+ uint8_t green;
+ uint8_t blue;
+ };
+
+/* The geometry of a chart. */
+struct chart_geometry
+ {
+ int data_top ;
+ int data_right ;
+ int data_bottom;
+ int data_left ;
+
+ int abscissa_top;
+
+ int ordinate_right ;
+
+ int title_bottom ;
+
+ /* Legend. */
+ int legend_left ;
+ int legend_right ;
+ const char **dataset;
+ int n_datasets;
+
+ /* Default font size for the plot. */
+ double font_size;
+
+ struct chart_colour fill_colour;
+
+ /* Stuff Particular to Cartesians (and Boxplots ) */
+ double ordinate_scale;
+ double abscissa_scale;
+ double x_min;
+ double x_max;
+ double y_min;
+ double y_max;
+ bool in_path;
+ };
+
+struct chart_class
+ {
+ void (*draw) (const struct chart *, cairo_t *, struct chart_geometry *);
+ void (*destroy) (struct chart *);
+ };
+
+struct chart
+ {
+ const struct chart_class *class;
+ int ref_cnt;
+ };
+
+void chart_init (struct chart *, const struct chart_class *);
+
+void chart_geometry_init (cairo_t *, struct chart_geometry *,
+ double width, double length);
+void chart_geometry_free (cairo_t *, struct chart_geometry *);
+
+void chart_draw (const struct chart *, cairo_t *, struct chart_geometry *);
+char *chart_draw_png (const struct chart *, const char *file_name_template,
+ int number);
+
+#endif /* output/chart-provider.h */
/* PSPP - a program for statistical analysis.
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <config.h>
#include <output/chart.h>
+#include <output/chart-provider.h>
#include <assert.h>
+#include <cairo/cairo.h>
#include <errno.h>
#include <float.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
-#include <plot.h>
-
#include <libpspp/str.h>
#include <output/manager.h>
#include <output/output.h>
extern struct som_table_class tab_table_class;
-struct chart *
-chart_create(void)
+void
+chart_init (struct chart *chart, const struct chart_class *class)
{
- struct chart *chart;
- struct outp_driver *d;
-
- d = outp_drivers (NULL);
- if (d == NULL)
- return NULL;
-
- chart = xmalloc (sizeof *chart);
- chart->lp = NULL;
- d->class->initialise_chart(d, chart);
- if (!chart->lp)
- {
- free (chart);
- return NULL;
- }
-
- if (pl_openpl_r (chart->lp) < 0) /* open Plotter */
- return NULL;
-
- pl_fspace_r (chart->lp, 0.0, 0.0, 1000.0, 1000.0); /* set coordinate system */
- pl_flinewidth_r (chart->lp, 0.25); /* set line thickness */
- pl_pencolorname_r (chart->lp, "black");
-
- pl_erase_r (chart->lp); /* erase graphics display */
- pl_filltype_r(chart->lp,0);
-
- pl_savestate_r(chart->lp);
-
- /* Set default chartetry */
- chart->data_top = 900;
- chart->data_right = 800;
- chart->data_bottom = 120;
- chart->data_left = 150;
- chart->abscissa_top = 70;
- chart->ordinate_right = 120;
- chart->title_bottom = 920;
- chart->legend_left = 810;
- chart->legend_right = 1000;
- chart->font_size = 0;
- chart->in_path = false;
- chart->dataset = NULL;
- chart->n_datasets = 0;
- strcpy(chart->fill_colour,"red");
-
- /* Get default font size */
- if ( !chart->font_size)
- chart->font_size = pl_fontsize_r(chart->lp, -1);
-
- /* Draw the data area */
- pl_box_r(chart->lp,
- chart->data_left, chart->data_bottom,
- chart->data_right, chart->data_top);
+ chart->class = class;
+ chart->ref_cnt = 1;
+}
- return chart;
+void
+chart_geometry_init (cairo_t *cr, struct chart_geometry *geom,
+ double width, double length)
+{
+ /* Set default chartetry. */
+ geom->data_top = 0.900 * length;
+ geom->data_right = 0.800 * width;
+ geom->data_bottom = 0.120 * length;
+ geom->data_left = 0.150 * width;
+ geom->abscissa_top = 0.070 * length;
+ geom->ordinate_right = 0.120 * width;
+ geom->title_bottom = 0.920 * length;
+ geom->legend_left = 0.810 * width;
+ geom->legend_right = width;
+ geom->font_size = 15.0;
+ geom->in_path = false;
+ geom->dataset = NULL;
+ geom->n_datasets = 0;
+
+ geom->fill_colour.red = 255;
+ geom->fill_colour.green = 0;
+ geom->fill_colour.blue = 0;
+
+ cairo_set_line_width (cr, 1.0);
+
+ cairo_rectangle (cr, geom->data_left, geom->data_bottom,
+ geom->data_right - geom->data_left,
+ geom->data_top - geom->data_bottom);
+ cairo_stroke (cr);
}
void
-chart_submit(struct chart *chart)
+chart_geometry_free (cairo_t *cr UNUSED, struct chart_geometry *geom)
{
int i;
- struct som_entity s;
- struct outp_driver *d;
-
- if ( ! chart )
- return ;
-
- pl_restorestate_r(chart->lp);
-
- s.class = &tab_table_class;
- s.ext = chart;
- s.type = SOM_CHART;
- som_submit (&s);
- if (pl_closepl_r (chart->lp) < 0) /* close Plotter */
- {
- fprintf (stderr, "Couldn't close Plotter\n");
- }
-
- pl_deletepl_r(chart->lp);
+ for (i = 0 ; i < geom->n_datasets; ++i)
+ free (geom->dataset[i]);
+ free (geom->dataset);
+}
- pl_deleteplparams(chart->pl_params);
+void
+chart_draw (const struct chart *chart, cairo_t *cr,
+ struct chart_geometry *geom)
+{
+ chart->class->draw (chart, cr, geom);
+}
- d = outp_drivers (NULL);
- d->class->finalise_chart(d, chart);
+char *
+chart_draw_png (const struct chart *chart, const char *file_name_template,
+ int number)
+{
+ const int width = 640;
+ const int length = 480;
+
+ struct chart_geometry geom;
+ cairo_surface_t *surface;
+ cairo_status_t status;
+ const char *number_pos;
+ char *file_name;
+ cairo_t *cr;
+
+ number_pos = strchr (file_name_template, '#');
+ if (number_pos != NULL)
+ file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
+ file_name_template, number, number_pos + 1);
+ else
+ file_name = xstrdup (file_name_template);
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
+ cr = cairo_create (surface);
+
+ cairo_translate (cr, 0.0, length);
+ cairo_scale (cr, 1.0, -1.0);
+
+ cairo_save (cr);
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_rectangle (cr, 0, 0, width, length);
+ cairo_fill (cr);
+ cairo_restore (cr);
+
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+
+ chart_geometry_init (cr, &geom, width, length);
+ chart_draw (chart, cr, &geom);
+ chart_geometry_free (cr, &geom);
+
+ status = cairo_surface_write_to_png (surface, file_name);
+ if (status != CAIRO_STATUS_SUCCESS)
+ error (0, 0, _("writing output file \"%s\": %s"),
+ file_name, cairo_status_to_string (status));
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+
+ return file_name;
+}
- for (i = 0 ; i < chart->n_datasets; ++i)
- free (chart->dataset[i]);
- free (chart->dataset);
- free(chart);
+struct chart *
+chart_ref (const struct chart *chart_)
+{
+ struct chart *chart = CONST_CAST (struct chart *, chart_);
+ chart->ref_cnt++;
+ return chart;
}
void
-chart_init_separate (struct chart *ch, const char *type,
- const char *file_name_tmpl, int number)
+chart_unref (struct chart *chart)
{
- FILE *fp;
- int number_pos;
-
- number_pos = strchr (file_name_tmpl, '#') - file_name_tmpl;
- ch->file_name = xasprintf ("%.*s%d%s",
- number_pos, file_name_tmpl,
- number,
- file_name_tmpl + number_pos + 1);
- fp = fopen (ch->file_name, "wb");
- if (fp == NULL)
+ if (chart != NULL)
{
- error (0, errno, _("creating \"%s\""), ch->file_name);
- free (ch->file_name);
- ch->file_name = NULL;
- return;
+ assert (chart->ref_cnt > 0);
+ if (--chart->ref_cnt == 0)
+ chart->class->destroy (chart);
}
-
- ch->pl_params = pl_newplparams ();
- ch->lp = pl_newpl_r (type, 0, fp, stderr, ch->pl_params);
}
void
-chart_finalise_separate (struct chart *ch)
+chart_submit (struct chart *chart)
{
- free (ch->file_name);
+#ifdef HAVE_CAIRO
+ struct outp_driver *d;
+
+ for (d = outp_drivers (NULL); d; d = outp_drivers (d))
+ if (d->class->output_chart != NULL)
+ d->class->output_chart (d, chart);
+#endif
+
+ chart_unref (chart);
}
/* PSPP - a program for statistical analysis.
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdio.h>
-#include <float.h>
-#include <assert.h>
-#include <math.h>
+#ifndef OUTPUT_CHART_H
+#define OUTPUT_CHART_H 1
-#include <math/chart-geometry.h>
-#include <libpspp/str.h>
-#include "manager.h"
-#include "output.h"
+#include <cairo/cairo.h>
-#include "xalloc.h"
+struct chart;
-#ifndef CHART_H
-#define CHART_H
+struct chart *chart_ref (const struct chart *);
+void chart_unref (struct chart *);
-#ifndef NO_CHARTS
-#include <plot.h>
-#endif
+void chart_submit (struct chart *);
-struct chart {
-
-#ifndef NO_CHARTS
- plPlotter *lp ;
- plPlotterParams *pl_params;
-#else
- void *lp;
-#endif
- char *file_name;
- FILE *file;
-
- /* The geometry of the chart
- See diagram at the foot of this file.
- */
-
- int data_top ;
- int data_right ;
- int data_bottom;
- int data_left ;
-
- int abscissa_top;
-
- int ordinate_right ;
-
- int title_bottom ;
-
- int legend_left ;
- int legend_right ;
- const char **dataset;
- int n_datasets;
-
-
- /* Default font size for the plot (if zero, then use plotter default) */
- int font_size;
-
- char fill_colour[10];
-
- /* Stuff Particular to Cartesians (and Boxplots ) */
- double ordinate_scale;
- double abscissa_scale;
- double x_min;
- double x_max;
- double y_min;
- double y_max;
- bool in_path;
-};
-
-
-
-struct chart * chart_create(void);
-void chart_submit(struct chart *ch);
-
-/* Helper functions for output drivers that put each chart into a
- separate file. */
-void chart_init_separate (struct chart *, const char *type,
- const char *file_name_tmpl, int number);
-
-void chart_finalise_separate (struct chart *);
-
-#endif
+#endif /* output/chart.h */
+++ /dev/null
-## 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
+++ /dev/null
-/* PSPP - a program for statistical analysis.
- Copyright (C) 2004, 2009 Free Software Foundation, Inc.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-
-#include <config.h>
-
-#include <stdio.h>
-#include <plot.h>
-#include <stdarg.h>
-#include <math.h>
-#include <output/charts/barchart.h>
-#include <output/chart.h>
-#include <output/charts/plot-chart.h>
-
-#define CATAGORIES 6
-#define SUB_CATAGORIES 3
-
-static const double x_min = 0;
-static const double x_max = 15.0;
-
-static const char *cat_labels[] =
- {
- "Age",
- "Intelligence",
- "Wealth",
- "Emotional",
- "cat 5",
- "cat 6",
- "cat 7",
- "cat 8",
- "cat 9",
- "cat 10",
- "cat 11"
- };
-
-
-
-
-/* Subcatagories */
-static const double data1[] =
-{
- 28,83,
- 34,
- 29,13,
- 9,4,
- 3,3,
- 2,0,
- 1,0,
- 0,
- 1,1
-};
-
-
-static const double data2[] =
-{
- 45,13,
- 9,4,
- 3,43,
- 2,0,
- 1,20,
- 0,0,
- 1,1,
- 0,0
-};
-
-static const double data3[] =
- {
- 23,18,
- 0, 45,23, 9, 40, 24,4, 8
- };
-
-
-static const char subcat_name[]="Gender";
-
-
-struct subcat {
- const double *data;
- const char *label;
-};
-
-static const struct subcat sub_catagory[SUB_CATAGORIES] =
- {
- {data1, "male"},
- {data2, "female"},
- {data3, "47xxy"}
- };
-
-
-
-static const double y_min = 0;
-static const double y_max = 120.0;
-static const double y_tick = 20.0;
-
-
-
-static void write_legend(struct chart *chart) ;
-
-
-void
-draw_barchart(struct chart *ch, const char *title,
- const char *xlabel, const char *ylabel, enum bar_opts opt)
-{
- double d;
- int i;
-
- double interval_size = fabs(ch->data_right - ch->data_left) / ( CATAGORIES );
-
- double bar_width = interval_size / 1.1 ;
-
- double ordinate_scale = fabs(ch->data_top - ch->data_bottom) /
- fabs(y_max - y_min) ;
-
- if ( opt != BAR_STACKED )
- bar_width /= SUB_CATAGORIES;
-
- /* Move to data bottom-left */
- pl_move_r(ch->lp, ch->data_left, ch->data_bottom);
-
- pl_savestate_r(ch->lp);
- pl_filltype_r(ch->lp,1);
-
- /* Draw the data */
- for (i = 0 ; i < CATAGORIES ; ++i )
- {
- int sc;
- double ystart=0.0;
- double x = i * interval_size;
-
- pl_savestate_r(ch->lp);
-
- draw_tick (ch, TICK_ABSCISSA, x + (interval_size/2 ), "%s",
- cat_labels[i]);
-
- for(sc = 0 ; sc < SUB_CATAGORIES ; ++sc )
- {
-
- pl_savestate_r(ch->lp);
- pl_fillcolorname_r(ch->lp,data_colour[sc % N_CHART_COLOURS]);
-
- switch ( opt )
- {
- case BAR_GROUPED:
- pl_fboxrel_r(ch->lp,
- x + (sc * bar_width ), 0,
- x + (sc + 1) * bar_width,
- sub_catagory[sc].data[i] * ordinate_scale );
- break;
-
-
- case BAR_STACKED:
-
- pl_fboxrel_r(ch->lp,
- x, ystart,
- x + bar_width,
- ystart + sub_catagory[sc].data[i] * ordinate_scale );
-
- ystart += sub_catagory[sc].data[i] * ordinate_scale ;
-
- break;
-
- default:
- break;
- }
- pl_restorestate_r(ch->lp);
- }
-
- pl_restorestate_r(ch->lp);
- }
- pl_restorestate_r(ch->lp);
-
- for ( d = y_min; d <= y_max ; d += y_tick )
- {
-
- draw_tick (ch, TICK_ORDINATE,
- (d - y_min ) * ordinate_scale, "%g", d);
-
- }
-
- /* Write the abscissa label */
- pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
- pl_alabel_r(ch->lp,0,'t',xlabel);
-
-
- /* Write the ordinate label */
- pl_savestate_r(ch->lp);
- pl_move_r(ch->lp,ch->data_bottom, ch->ordinate_right);
- pl_textangle_r(ch->lp,90);
- pl_alabel_r(ch->lp,0,0,ylabel);
- pl_restorestate_r(ch->lp);
-
-
- chart_write_title(ch, "%s", title);
-
- write_legend(ch);
-
-
-}
-
-
-
-
-
-static void
-write_legend(struct chart *chart)
-{
- int sc;
-
- pl_savestate_r(chart->lp);
-
- pl_filltype_r(chart->lp,1);
-
- pl_move_r(chart->lp, chart->legend_left,
- chart->data_bottom + chart->font_size * SUB_CATAGORIES * 1.5);
-
- pl_alabel_r(chart->lp,0,'b',subcat_name);
-
- for (sc = 0 ; sc < SUB_CATAGORIES ; ++sc )
- {
- pl_fmove_r(chart->lp,
- chart->legend_left,
- chart->data_bottom + chart->font_size * sc * 1.5);
-
- pl_savestate_r(chart->lp);
- pl_fillcolorname_r(chart->lp,data_colour[sc]);
- pl_fboxrel_r (chart->lp,
- 0,0,
- chart->font_size, chart->font_size);
- pl_restorestate_r(chart->lp);
-
- pl_fmove_r(chart->lp,
- chart->legend_left + chart->font_size * 1.5,
- chart->data_bottom + chart->font_size * sc * 1.5);
-
- pl_alabel_r(chart->lp,'l','b',sub_catagory[sc].label);
- }
-
-
- pl_restorestate_r(chart->lp);
-}
+++ /dev/null
-/* PSPP - a program for statistical analysis.
- Copyright (C) 2004 Free Software Foundation, Inc.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef BARCHART_H
-#define BARCHART_H
-
-#include <output/chart.h>
-
-enum bar_opts {
- BAR_GROUPED = 0,
- BAR_STACKED,
- BAR_RANGE
-};
-
-void draw_barchart(struct chart *ch, const char *title,
- const char *xlabel, const char *ylabel, enum bar_opts opt);
-
-#endif
#include <config.h>
+#include <output/charts/box-whisker.h>
+
#include <math.h>
#include <assert.h>
-#include <libpspp/misc.h>
-
-#include <output/charts/box-whisker.h>
-#include <output/charts/plot-chart.h>
+#include <cairo/cairo.h>
-#include <output/chart.h>
+#include <libpspp/cast.h>
+#include <libpspp/misc.h>
#include <math/chart-geometry.h>
#include <math/box-whisker.h>
+#include <output/chart.h>
+#include <output/chart-provider.h>
+#include <output/charts/plot-chart.h>
/* Draw a box-and-whiskers plot
*/
+struct box
+ {
+ struct box_whisker *bw;
+ char *label;
+ };
+
+struct boxplot
+ {
+ struct chart chart;
+ double y_min;
+ double y_max;
+ char *title;
+ struct box *boxes;
+ size_t n_boxes, boxes_allocated;
+ };
+
+static const struct chart_class boxplot_chart_class;
+
+struct boxplot *
+boxplot_create (double y_min, double y_max, const char *title)
+{
+ struct boxplot *boxplot = xmalloc (sizeof *boxplot);
+ chart_init (&boxplot->chart, &boxplot_chart_class);
+ boxplot->y_min = y_min;
+ boxplot->y_max = y_max;
+ boxplot->title = xstrdup (title);
+ boxplot->boxes = NULL;
+ boxplot->n_boxes = boxplot->boxes_allocated = 0;
+ return boxplot;
+}
+
+void
+boxplot_add_box (struct boxplot *boxplot,
+ struct box_whisker *bw, const char *label)
+{
+ struct box *box;
+ if (boxplot->n_boxes >= boxplot->boxes_allocated)
+ boxplot->boxes = x2nrealloc (boxplot->boxes, &boxplot->boxes_allocated,
+ sizeof *boxplot->boxes);
+ box = &boxplot->boxes[boxplot->n_boxes++];
+ box->bw = bw;
+ box->label = xstrdup (label);
+}
+
+struct chart *
+boxplot_get_chart (struct boxplot *boxplot)
+{
+ return &boxplot->chart;
+}
+
/* Draw an OUTLIER on the plot CH
* at CENTRELINE
*/
static void
-draw_case (struct chart *ch, double centreline,
+draw_case (cairo_t *cr, const struct chart_geometry *geom, double centreline,
const struct outlier *outlier)
{
+ double y = geom->data_bottom + (outlier->value - geom->y_min) * geom->ordinate_scale;
+ chart_draw_marker (cr, centreline, y,
+ outlier->extreme ? MARKER_ASTERISK : MARKER_CIRCLE,
+ 20);
-#define MARKER_CIRCLE 4
-#define MARKER_STAR 3
-
- pl_fmarker_r(ch->lp,
- centreline,
- ch->data_bottom + (outlier->value - ch->y_min) * ch->ordinate_scale,
- outlier->extreme ? MARKER_STAR : MARKER_CIRCLE,
- 20);
-
- pl_moverel_r(ch->lp, 10,0);
-
- pl_alabel_r(ch->lp, 'l', 'c', ds_cstr (&outlier->label));
+ cairo_move_to (cr, centreline + 10, y);
+ chart_label (cr, 'l', 'c', geom->font_size, ds_cstr (&outlier->label));
}
-
-void
-boxplot_draw_boxplot (struct chart *ch,
- double box_centre,
- double box_width,
- const struct box_whisker *bw,
- const char *name)
+static void
+boxplot_draw_box (cairo_t *cr, const struct chart_geometry *geom,
+ double box_centre,
+ double box_width,
+ const struct box_whisker *bw,
+ const char *name)
{
double whisker[2];
double hinge[3];
box_whisker_whiskers (bw, whisker);
box_whisker_hinges (bw, hinge);
- box_bottom = ch->data_bottom + (hinge[0] - ch->y_min ) * ch->ordinate_scale;
-
- box_top = ch->data_bottom + (hinge[2] - ch->y_min ) * ch->ordinate_scale;
+ box_bottom = geom->data_bottom + (hinge[0] - geom->y_min ) * geom->ordinate_scale;
- bottom_whisker = ch->data_bottom + (whisker[0] - ch->y_min) *
- ch->ordinate_scale;
+ box_top = geom->data_bottom + (hinge[2] - geom->y_min ) * geom->ordinate_scale;
- top_whisker = ch->data_bottom + (whisker[1] - ch->y_min) * ch->ordinate_scale;
+ bottom_whisker = geom->data_bottom + (whisker[0] - geom->y_min) *
+ geom->ordinate_scale;
- pl_savestate_r(ch->lp);
+ top_whisker = geom->data_bottom + (whisker[1] - geom->y_min) * geom->ordinate_scale;
/* Draw the box */
- pl_savestate_r (ch->lp);
- pl_fillcolorname_r (ch->lp, ch->fill_colour);
- pl_filltype_r (ch->lp,1);
- pl_fbox_r (ch->lp,
- box_left,
- box_bottom,
- box_right,
- box_top);
-
- pl_restorestate_r (ch->lp);
+ cairo_rectangle (cr,
+ box_left,
+ box_bottom,
+ box_right - box_left,
+ box_top - box_bottom);
+ cairo_save (cr);
+ cairo_set_source_rgb (cr,
+ geom->fill_colour.red / 255.0,
+ geom->fill_colour.green / 255.0,
+ geom->fill_colour.blue / 255.0);
+ cairo_fill (cr);
+ cairo_restore (cr);
+ cairo_stroke (cr);
/* Draw the median */
- pl_savestate_r (ch->lp);
- pl_linewidth_r (ch->lp, 5);
- pl_fline_r (ch->lp,
- box_left,
- ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale,
- box_right,
- ch->data_bottom + (hinge[1] - ch->y_min) * ch->ordinate_scale);
- pl_restorestate_r (ch->lp);
+ cairo_save (cr);
+ cairo_set_line_width (cr, cairo_get_line_width (cr) * 5);
+ cairo_move_to (cr,
+ box_left,
+ geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale);
+ cairo_line_to (cr,
+ box_right,
+ geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale);
+ cairo_stroke (cr);
+ cairo_restore (cr);
/* Draw the bottom whisker */
- pl_fline_r (ch->lp,
- box_left,
- bottom_whisker,
- box_right,
- bottom_whisker);
+ cairo_move_to (cr, box_left, bottom_whisker);
+ cairo_line_to (cr, box_right, bottom_whisker);
+ cairo_stroke (cr);
/* Draw top whisker */
- pl_fline_r (ch->lp,
- box_left,
- top_whisker,
- box_right,
- top_whisker);
-
+ cairo_move_to (cr, box_left, top_whisker);
+ cairo_line_to (cr, box_right, top_whisker);
+ cairo_stroke (cr);
/* Draw centre line.
(bottom half) */
- pl_fline_r (ch->lp,
- box_centre, bottom_whisker,
- box_centre, box_bottom);
+ cairo_move_to (cr, box_centre, bottom_whisker);
+ cairo_line_to (cr, box_centre, box_bottom);
+ cairo_stroke (cr);
/* (top half) */
- pl_fline_r (ch->lp,
- box_centre, top_whisker,
- box_centre, box_top);
+ cairo_move_to (cr, box_centre, top_whisker);
+ cairo_line_to (cr, box_centre, box_top);
+ cairo_stroke (cr);
outliers = box_whisker_outliers (bw);
for (ll = ll_head (outliers);
ll != ll_null (outliers); ll = ll_next (ll))
{
const struct outlier *outlier = ll_data (ll, struct outlier, ll);
- draw_case (ch, box_centre, outlier);
+ draw_case (cr, geom, box_centre, outlier);
}
/* Draw tick mark on x axis */
- draw_tick(ch, TICK_ABSCISSA, box_centre - ch->data_left, "%s", name);
-
- pl_restorestate_r(ch->lp);
+ draw_tick(cr, geom, TICK_ABSCISSA, box_centre - geom->data_left, "%s", name);
}
-void
-boxplot_draw_yscale (struct chart *ch, double y_max, double y_min)
+static void
+boxplot_draw_yscale (cairo_t *cr, struct chart_geometry *geom,
+ double y_max, double y_min)
{
double y_tick;
double d;
- if ( !ch )
- return ;
+ geom->y_max = y_max;
+ geom->y_min = y_min;
+
+ y_tick = chart_rounded_tick (fabs (geom->y_max - geom->y_min) / 5.0);
- ch->y_max = y_max;
- ch->y_min = y_min;
+ geom->y_min = (ceil (geom->y_min / y_tick) - 1.0) * y_tick;
- y_tick = chart_rounded_tick (fabs(ch->y_max - ch->y_min) / 5.0);
+ geom->y_max = (floor (geom->y_max / y_tick) + 1.0) * y_tick;
- ch->y_min = (ceil( ch->y_min / y_tick ) - 1.0 ) * y_tick;
+ geom->ordinate_scale = (fabs (geom->data_top - geom->data_bottom)
+ / fabs (geom->y_max - geom->y_min));
- ch->y_max = ( floor( ch->y_max / y_tick ) + 1.0 ) * y_tick;
+ for (d = geom->y_min; d <= geom->y_max; d += y_tick)
+ draw_tick (cr, geom, TICK_ORDINATE,
+ (d - geom->y_min) * geom->ordinate_scale, "%g", d);
+}
- ch->ordinate_scale = fabs(ch->data_top - ch->data_bottom)
- / fabs(ch->y_max - ch->y_min) ;
+static void
+boxplot_chart_draw (const struct chart *chart, cairo_t *cr,
+ struct chart_geometry *geom)
+{
+ const struct boxplot *boxplot = UP_CAST (chart, struct boxplot, chart);
+ double box_width;
+ size_t i;
- /* Move to data bottom-left */
- pl_move_r(ch->lp,
- ch->data_left, ch->data_bottom);
+ boxplot_draw_yscale (cr, geom, boxplot->y_max, boxplot->y_min);
+ chart_write_title (cr, geom, "%s", boxplot->title);
- for ( d = ch->y_min; d <= ch->y_max ; d += y_tick )
+ box_width = (geom->data_right - geom->data_left) / boxplot->n_boxes / 2.0;
+ for (i = 0; i < boxplot->n_boxes; i++)
{
- draw_tick (ch, TICK_ORDINATE, (d - ch->y_min ) * ch->ordinate_scale, "%g", d);
+ const struct box *box = &boxplot->boxes[i];
+ const double box_centre = (i * 2 + 1) * box_width + geom->data_left;
+ boxplot_draw_box (cr, geom, box_centre, box_width, box->bw, box->label);
}
}
+
+static void
+boxplot_chart_destroy (struct chart *chart)
+{
+ struct boxplot *boxplot = UP_CAST (chart, struct boxplot, chart);
+ size_t i;
+
+ free (boxplot->title);
+ for (i = 0; i < boxplot->n_boxes; i++)
+ {
+ struct box *box = &boxplot->boxes[i];
+ struct statistic *statistic = &box->bw->parent.parent;
+ statistic->destroy (statistic);
+ free (box->label);
+ }
+ free (boxplot->boxes);
+ free (boxplot);
+}
+
+static const struct chart_class boxplot_chart_class =
+ {
+ boxplot_chart_draw,
+ boxplot_chart_destroy
+ };
/* PSPP - a program for statistical analysis.
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#ifndef BOX_WHISKER_H
#define BOX_WHISKER_H
-struct chart ;
struct box_whisker;
-void boxplot_draw_boxplot (struct chart *ch,
- double box_centre,
- double box_width,
- const struct box_whisker *w,
- const char *name);
-
-
-void boxplot_draw_yscale (struct chart *ch , double y_max, double y_min);
+struct boxplot *boxplot_create (double y_min, double y_max, const char *title);
+void boxplot_add_box (struct boxplot *,
+ struct box_whisker *, const char *label);
+struct chart *boxplot_get_chart (struct boxplot *);
#endif
/* PSPP - a program for statistical analysis.
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <config.h>
+#include <output/charts/cartesian.h>
+
+#include <cairo/cairo.h>
#include <math.h>
#include <assert.h>
#include <output/chart.h>
-
+#include <output/chart-provider.h>
#include <output/charts/plot-chart.h>
-#include <output/charts/cartesian.h>
#include <libpspp/compiler.h>
+#include "xalloc.h"
/* Start a new vector called NAME */
void
-chart_vector_start (struct chart *ch, const char *name)
+chart_vector_start (cairo_t *cr, struct chart_geometry *geom, const char *name)
{
- if ( ! ch )
- return ;
+ const struct chart_colour *colour;
- pl_savestate_r (ch->lp);
+ cairo_save (cr);
- pl_colorname_r (ch->lp, data_colour [ch->n_datasets % N_CHART_COLOURS]);
+ colour = &data_colour[geom->n_datasets % N_CHART_COLOURS];
+ cairo_set_source_rgb (cr,
+ colour->red / 255.0,
+ colour->green / 255.0,
+ colour->blue / 255.0);
- ch->n_datasets++;
- ch->dataset = xrealloc (ch->dataset, ch->n_datasets * sizeof (*ch->dataset));
+ geom->n_datasets++;
+ geom->dataset = xrealloc (geom->dataset,
+ geom->n_datasets * sizeof (*geom->dataset));
- ch->dataset[ch->n_datasets - 1] = strdup (name);
+ geom->dataset[geom->n_datasets - 1] = strdup (name);
}
/* Plot a data point */
void
-chart_datum (struct chart *ch, int dataset UNUSED, double x, double y)
+chart_datum (cairo_t *cr, const struct chart_geometry *geom,
+ int dataset UNUSED, double x, double y)
{
- if ( ! ch )
- return ;
-
- {
- const double x_pos =
- (x - ch->x_min) * ch->abscissa_scale + ch->data_left ;
+ double x_pos = (x - geom->x_min) * geom->abscissa_scale + geom->data_left;
+ double y_pos = (y - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
- const double y_pos =
- (y - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
-
- pl_savestate_r(ch->lp);
-
- pl_fmarker_r(ch->lp, x_pos, y_pos, 6, 15);
-
- pl_restorestate_r(ch->lp);
- }
+ chart_draw_marker (cr, x_pos, y_pos, MARKER_SQUARE, 15);
}
void
-chart_vector_end (struct chart *ch)
+chart_vector_end (cairo_t *cr, struct chart_geometry *geom)
{
- pl_endpath_r (ch->lp);
- pl_colorname_r (ch->lp, "black");
- ch->in_path = false;
- pl_restorestate_r (ch->lp);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+ geom->in_path = false;
}
/* Plot a data point */
void
-chart_vector (struct chart *ch, double x, double y)
+chart_vector (cairo_t *cr, struct chart_geometry *geom, double x, double y)
{
- if ( ! ch )
- return ;
-
- {
- const double x_pos =
- (x - ch->x_min) * ch->abscissa_scale + ch->data_left ;
-
- const double y_pos =
- (y - ch->y_min) * ch->ordinate_scale + ch->data_bottom ;
-
- if ( ch->in_path)
- pl_fcont_r (ch->lp, x_pos, y_pos);
- else
- {
- pl_fmove_r (ch->lp, x_pos, y_pos);
- ch->in_path = true;
- }
- }
+ const double x_pos =
+ (x - geom->x_min) * geom->abscissa_scale + geom->data_left ;
+
+ const double y_pos =
+ (y - geom->y_min) * geom->ordinate_scale + geom->data_bottom ;
+
+ if (geom->in_path)
+ cairo_line_to (cr, x_pos, y_pos);
+ else
+ {
+ cairo_move_to (cr, x_pos, y_pos);
+ geom->in_path = true;
+ }
}
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;
}
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);
}
/* PSPP - a program for statistical analysis.
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#ifndef CARTESIAN_H
#define CARTESIAN_H
+#include <cairo/cairo.h>
+#include <libpspp/compiler.h>
+#include <output/chart.h>
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);
+++ /dev/null
-/* PSPP - a program for statistical analysis.
- Copyright (C) 2005 Free Software Foundation, Inc.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-
-/* Stubs for plotting routines.
- This module is linked only when charts are not supported */
-
-#include "config.h"
-#include <output/chart.h>
-#include <output/charts/box-whisker.h>
-#include <output/charts/piechart.h>
-#include <output/charts/plot-chart.h>
-#include <output/charts/plot-hist.h>
-#include <output/charts/cartesian.h>
-#include <gsl/gsl_histogram.h>
-#include <libpspp/compiler.h>
-
-#ifndef NO_CHARTS
-#error This file should be used only when compiling without charts.
-#endif
-
-void
-chart_write_title (struct chart *chart UNUSED, const char *title UNUSED, ...)
-{
-}
-
-
-void
-chart_write_xscale (struct chart *ch UNUSED,
- double min UNUSED, double max UNUSED, int ticks UNUSED)
-{
-}
-
-
-void
-chart_write_yscale (struct chart *ch UNUSED UNUSED,
- double smin UNUSED, double smax UNUSED, int ticks UNUSED)
-{
-}
-
-
-void
-chart_write_xlabel (struct chart *ch UNUSED, const char *label UNUSED)
-{
-}
-
-void
-chart_write_ylabel (struct chart *ch UNUSED, const char *label UNUSED)
-{
-}
-
-
-void
-chart_line (struct chart *ch UNUSED,
- double slope UNUSED, double intercept UNUSED,
- double limit1 UNUSED, double limit2 UNUSED,
- enum CHART_DIM lim_dim UNUSED)
-{
-}
-
-
-void
-chart_datum (struct chart *ch UNUSED, int dataset UNUSED UNUSED,
- double x UNUSED, double y UNUSED)
-{
-}
-
-void
-histogram_plot (const struct histogram *hist UNUSED,
- const char *label UNUSED,
- const struct moments1 *m UNUSED)
-{
-}
-
-void
-histogram_plot_n (const struct histogram *hist UNUSED,
- const char *label UNUSED,
- double n UNUSED, double mean UNUSED, double stddev UNUSED,
- bool show_normal UNUSED)
-{
-}
-
-
-void
-boxplot_draw_yscale (struct chart *ch UNUSED,
- double y_max UNUSED, double y_min UNUSED)
-{
-}
-
-void
-boxplot_draw_boxplot (struct chart *ch UNUSED,
- double box_centre UNUSED,
- double box_width UNUSED,
- const struct box_whisker *w UNUSED,
- const char *name UNUSED)
-{
-}
-
-
-
-
-void
-piechart_plot (const char *title UNUSED,
- const struct slice *slices UNUSED, int n_slices UNUSED)
-{
-}
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2004, 2008, 2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/charts/np-plot.h>
+
+#include <gsl/gsl_cdf.h>
+
+#include <data/casereader.h>
+#include <data/casewriter.h>
+#include <libpspp/cast.h>
+#include <libpspp/message.h>
+#include <math/np.h>
+#include <output/chart-provider.h>
+#include <output/charts/cartesian.h>
+#include <output/charts/plot-chart.h>
+
+#include "gl/minmax.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* An NP or DNP plot. */
+struct np_plot_chart
+ {
+ struct chart chart;
+ char *label;
+ struct casereader *data;
+
+ /* Copied directly from struct np. */
+ double y_min, y_max;
+ double dns_min, dns_max;
+
+ /* Calculated. */
+ double slope, intercept;
+ double y_first, y_last;
+ double x_lower, x_upper;
+ double slack;
+ };
+
+static const struct chart_class np_plot_chart_class;
+static const struct chart_class dnp_plot_chart_class;
+
+static struct chart *
+make_np_plot (const struct chart_class *class,
+ const struct np *np, const struct casereader *reader,
+ const char *label)
+{
+ struct np_plot_chart *npp;
+
+ if (np->n < 1.0)
+ return NULL;
+
+ npp = xmalloc (sizeof *npp);
+ chart_init (&npp->chart, class);
+ npp->label = xstrdup (label);
+ npp->data = casereader_clone (reader);
+ npp->y_min = np->y_min;
+ npp->y_max = np->y_max;
+ npp->dns_min = np->dns_min;
+ npp->dns_max = np->dns_max;
+
+ /* Slope and intercept of the ideal normal probability line. */
+ npp->slope = 1.0 / np->stddev;
+ npp->intercept = -np->mean / np->stddev;
+
+ npp->y_first = gsl_cdf_ugaussian_Pinv (1 / (np->n + 1));
+ npp->y_last = gsl_cdf_ugaussian_Pinv (np->n / (np->n + 1));
+
+ /* Need to make sure that both the scatter plot and the ideal fit into the
+ plot. */
+ npp->x_lower = MIN (np->y_min, (npp->y_first - npp->intercept) / npp->slope);
+ npp->x_upper = MAX (np->y_max, (npp->y_last - npp->intercept) / npp->slope);
+ npp->slack = (npp->x_upper - npp->x_lower) * 0.05;
+
+ return &npp->chart;
+}
+
+/* Creates and returns a normal probability plot corresponding to
+ the calculations in NP and the data in READER, and label the
+ plot with LABEL. The data in READER must have Y-values in
+ value index NP_IDX_Y and NS-values in value index NP_IDX_NS.
+
+ Returns a null pointer if the data set is empty.
+
+ The caller retains ownership of NP and READER. */
+struct chart *
+np_plot_create (const struct np *np, const struct casereader *reader,
+ const char *label)
+{
+ return make_np_plot (&np_plot_chart_class, np, reader, label);
+}
+
+/* Creates and returns a detrended normal probability plot
+ corresponding to the calculations in NP and the data in
+ READER, and label the plot with LABEL. The data in READER
+ must have Y-values in value index NP_IDX_Y and DNS-values in
+ value index NP_IDX_DNS.
+
+ Returns a null pointer if the data set is empty.
+
+ The caller retains ownership of NP and READER. */
+struct chart *
+dnp_plot_create (const struct np *np, const struct casereader *reader,
+ const char *label)
+{
+ return make_np_plot (&dnp_plot_chart_class, np, reader, label);
+}
+
+static void
+np_plot_chart_draw (const struct chart *chart, cairo_t *cr,
+ struct chart_geometry *geom)
+{
+ const struct np_plot_chart *npp = UP_CAST (chart, struct np_plot_chart,
+ chart);
+ struct casereader *data;
+ struct ccase *c;
+
+ chart_write_title (cr, geom, _("Normal Q-Q Plot of %s"), npp->label);
+ chart_write_xlabel (cr, geom, _("Observed Value"));
+ chart_write_ylabel (cr, geom, _("Expected Normal"));
+ chart_write_xscale (cr, geom,
+ npp->x_lower - npp->slack,
+ npp->x_upper + npp->slack, 5);
+ chart_write_yscale (cr, geom, npp->y_first, npp->y_last, 5);
+
+ data = casereader_clone (npp->data);
+ for (; (c = casereader_read (data)) != NULL; case_unref (c))
+ chart_datum (cr, geom, 0,
+ case_data_idx (c, NP_IDX_Y)->f,
+ case_data_idx (c, NP_IDX_NS)->f);
+ casereader_destroy (data);
+
+ chart_line (cr, geom, npp->slope, npp->intercept,
+ npp->y_first, npp->y_last, CHART_DIM_Y);
+}
+
+static void
+dnp_plot_chart_draw (const struct chart *chart, cairo_t *cr,
+ struct chart_geometry *geom)
+{
+ const struct np_plot_chart *dnpp = UP_CAST (chart, struct np_plot_chart,
+ chart);
+ struct casereader *data;
+ struct ccase *c;
+
+ chart_write_title (cr, geom, _("Detrended Normal Q-Q Plot of %s"),
+ dnpp->label);
+ chart_write_xlabel (cr, geom, _("Observed Value"));
+ chart_write_ylabel (cr, geom, _("Dev from Normal"));
+ chart_write_xscale (cr, geom, dnpp->y_min, dnpp->y_max, 5);
+ chart_write_yscale (cr, geom, dnpp->dns_min, dnpp->dns_max, 5);
+
+ data = casereader_clone (dnpp->data);
+ for (; (c = casereader_read (data)) != NULL; case_unref (c))
+ chart_datum (cr, geom, 0, case_data_idx (c, NP_IDX_Y)->f,
+ case_data_idx (c, NP_IDX_DNS)->f);
+ casereader_destroy (data);
+
+ chart_line (cr, geom, 0, 0, dnpp->y_min, dnpp->y_max, CHART_DIM_X);
+}
+
+static void
+np_plot_chart_destroy (struct chart *chart)
+{
+ struct np_plot_chart *npp = UP_CAST (chart, struct np_plot_chart, chart);
+ casereader_destroy (npp->data);
+ free (npp->label);
+ free (npp);
+}
+
+static const struct chart_class np_plot_chart_class =
+ {
+ np_plot_chart_draw,
+ np_plot_chart_destroy
+ };
+
+static const struct chart_class dnp_plot_chart_class =
+ {
+ dnp_plot_chart_draw,
+ np_plot_chart_destroy
+ };
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2004, 2008, 2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CHARTS_NP_PLOT_H
+#define OUTPUT_CHARTS_NP_PLOT_H 1
+
+struct casereader;
+struct np;
+
+struct chart *np_plot_create (const struct np *, const struct casereader *,
+ const char *label);
+struct chart *dnp_plot_create (const struct np *, const struct casereader *,
+ const char *label);
+
+#endif /* output/charts/np-plot.h */
#include <config.h>
-#include <float.h>
+#include <output/charts/piechart.h>
+
#include <assert.h>
+#include <float.h>
+#include <gsl/gsl_math.h>
#include <math.h>
#include <stdio.h>
-
-#include <output/charts/piechart.h>
-#include <output/charts/plot-chart.h>
-
-#include <output/chart.h>
-#include <libpspp/str.h>
#include <data/value-labels.h>
+#include <libpspp/cast.h>
+#include <libpspp/str.h>
+#include <output/charts/plot-chart.h>
+#include <output/chart-provider.h>
#include "minmax.h"
+struct piechart
+ {
+ struct chart chart;
+ char *title;
+ struct slice *slices;
+ int n_slices;
+ };
-/* Pie charts of course need to know Pi :) */
-#ifndef M_PI
-#define M_PI ( 22.0 / 7.0 )
-#endif
-
-
+static const struct chart_class piechart_class;
/* Draw a single slice of the pie */
static void
-draw_segment(struct chart *ch,
+draw_segment(cairo_t *,
double centre_x, double centre_y,
double radius,
double start_angle, double segment_angle,
- const char *colour) ;
+ const struct chart_colour *) ;
-/* Draw a piechart */
-void
-piechart_plot(const char *title, const struct slice *slices, int n_slices)
+/* Creates and returns a chart that will render a piechart with
+ the given TITLE and the N_SLICES described in SLICES. */
+struct chart *
+piechart_create (const char *title, const struct slice *slices, int n_slices)
{
+ struct piechart *pie;
int i;
- double total_magnetude=0;
-
- struct chart *ch;
- double left_label;
- double right_label;
+ pie = xmalloc (sizeof *pie);
+ chart_init (&pie->chart, &piechart_class);
+ pie->title = xstrdup (title);
+ pie->slices = xnmalloc (n_slices, sizeof *pie->slices);
+ for (i = 0; i < n_slices; i++)
+ {
+ const struct slice *src = &slices[i];
+ struct slice *dst = &pie->slices[i];
- double centre_x;
- double centre_y;
+ ds_init_string (&dst->label, &src->label);
+ dst->magnitude = src->magnitude;
+ }
+ pie->n_slices = n_slices;
+ return &pie->chart;
+}
+static void
+piechart_draw (const struct chart *chart, cairo_t *cr,
+ struct chart_geometry *geom)
+{
+ const struct piechart *pie = UP_CAST (chart, struct piechart, chart);
+ double total_magnitude;
+ double left_label, right_label;
+ double centre_x, centre_y;
double radius;
+ double angle;
+ int i;
- ch = chart_create ();
- if (ch == NULL)
- return;
+ centre_x = (geom->data_right + geom->data_left) / 2.0 ;
+ centre_y = (geom->data_top + geom->data_bottom) / 2.0 ;
- left_label = ch->data_left + (ch->data_right - ch->data_left)/10.0;
- right_label = ch->data_right - (ch->data_right - ch->data_left)/10.0;
- centre_x = (ch->data_right + ch->data_left ) / 2.0;
- centre_y = (ch->data_top + ch->data_bottom ) / 2.0;
- radius = MIN (5.0 / 12.0 * (ch->data_top - ch->data_bottom),
- 1.0 / 4.0 * (ch->data_right - ch->data_left));
+ left_label = geom->data_left + (geom->data_right - geom->data_left)/10.0;
+ right_label = geom->data_right - (geom->data_right - geom->data_left)/10.0;
- chart_write_title(ch, "%s", title);
+ radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom),
+ 1.0 / 4.0 * (geom->data_right - geom->data_left));
- for (i = 0 ; i < n_slices ; ++i )
- total_magnetude += slices[i].magnetude;
+ radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom),
+ 1.0 / 4.0 * (geom->data_right - geom->data_left));
- for (i = 0 ; i < n_slices ; ++i )
- {
- static double angle=0.0;
+ chart_write_title (cr, geom, "%s", pie->title);
+ total_magnitude = 0.0;
+ for (i = 0; i < pie->n_slices; i++)
+ total_magnitude += pie->slices[i].magnitude;
+
+ angle = 0.0;
+ for (i = 0; i < pie->n_slices ; ++i )
+ {
const double segment_angle =
- slices[i].magnetude / total_magnetude * 2 * M_PI ;
+ pie->slices[i].magnitude / total_magnitude * 2 * M_PI ;
const double label_x = centre_x -
radius * sin(angle + segment_angle/2.0);
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
+ };
/* PSPP - a program for statistical analysis.
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
struct slice {
struct string label;
- double magnetude;
+ double magnitude;
};
-/* Draw a piechart */
-void piechart_plot(const char *title,
- const struct slice *slices, int n_slices);
+struct chart *piechart_create (const char *title,
+ const struct slice *, int n_slices);
#endif
#include <config.h>
-#include <stdio.h>
-#include <plot.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdio.h>
-#include <float.h>
-#include <assert.h>
-#include <math.h>
-
#include <output/charts/plot-chart.h>
-#include <math/chart-geometry.h>
-
-
+#include <assert.h>
+#include <float.h>
+#include <math.h>
+#include <pango/pango-font.h>
+#include <pango/pango-layout.h>
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdio.h>
+#include <string.h>
-#include <libpspp/str.h>
#include <libpspp/assertion.h>
+#include <libpspp/str.h>
+#include <math/chart-geometry.h>
+#include <output/chart-provider.h>
#include <output/manager.h>
#include <output/output.h>
#include "xalloc.h"
-const char *const data_colour[N_CHART_COLOURS] =
+#if ! PANGO_VERSION_CHECK (2, 22, 0)
+int pango_layout_get_baseline (PangoLayout *layout);
+
+/* Shamelessly copied from the pango source */
+int
+pango_layout_get_baseline (PangoLayout *layout)
+{
+ int baseline;
+
+ /* XXX this is so inefficient */
+ PangoLayoutIter *iter = pango_layout_get_iter (layout);
+ baseline = pango_layout_iter_get_baseline (iter);
+ pango_layout_iter_free (iter);
+
+ return baseline;
+}
+#endif
+
+
+
+const struct chart_colour data_colour[N_CHART_COLOURS] =
{
- "brown",
- "red",
- "orange",
- "yellow",
- "green",
- "blue",
- "violet",
- "grey",
- "pink"
+ { 165, 42, 42 }, /* brown */
+ { 255, 0, 0 }, /* red */
+ { 255, 165, 0 }, /* orange */
+ { 255, 255, 0 }, /* yellow */
+ { 0, 255, 0 }, /* green */
+ { 0, 0, 255 }, /* blue */
+ { 238, 130, 238 }, /* violet */
+ { 190, 190, 190 }, /* grey */
+ { 255, 192, 203 }, /* pink */
};
+void
+chart_draw_marker (cairo_t *cr, double x, double y, enum marker_type marker,
+ double size)
+{
+ cairo_save (cr);
+ cairo_translate (cr, x, y);
+ cairo_scale (cr, size / 2.0, size / 2.0);
+ cairo_set_line_width (cr, cairo_get_line_width (cr) / (size / 2.0));
+ switch (marker)
+ {
+ case MARKER_CIRCLE:
+ cairo_arc (cr, 0, 0, 1.0, 0, 2 * M_PI);
+ cairo_stroke (cr);
+ break;
+
+ case MARKER_ASTERISK:
+ cairo_move_to (cr, 0, -1.0); /* | */
+ cairo_line_to (cr, 0, 1.0);
+ cairo_move_to (cr, -M_SQRT1_2, -M_SQRT1_2); /* / */
+ cairo_line_to (cr, M_SQRT1_2, M_SQRT1_2);
+ cairo_move_to (cr, -M_SQRT1_2, M_SQRT1_2); /* \ */
+ cairo_line_to (cr, M_SQRT1_2, -M_SQRT1_2);
+ cairo_stroke (cr);
+ break;
+
+ case MARKER_SQUARE:
+ cairo_rectangle (cr, -1.0, -1.0, 2.0, 2.0);
+ cairo_stroke (cr);
+ break;
+ }
+ cairo_restore (cr);
+}
+void
+chart_label (cairo_t *cr, int horz_justify, int vert_justify, double font_size,
+ const char *string)
+{
+ PangoFontDescription *desc;
+ PangoLayout *layout;
+ double x, y;
+
+ desc = pango_font_description_from_string ("sans serif");
+ if (desc == NULL)
+ {
+ cairo_new_path (cr);
+ return;
+ }
+ pango_font_description_set_absolute_size (desc, font_size * PANGO_SCALE);
+
+ cairo_save (cr);
+ cairo_get_current_point (cr, &x, &y);
+ cairo_translate (cr, x, y);
+ cairo_move_to (cr, 0, 0);
+ cairo_scale (cr, 1.0, -1.0);
+
+ layout = pango_cairo_create_layout (cr);
+ pango_layout_set_font_description (layout, desc);
+ pango_layout_set_text (layout, string, -1);
+ if (horz_justify != 'l')
+ {
+ int width_pango;
+ double width;
+
+ pango_layout_get_size (layout, &width_pango, NULL);
+ width = (double) width_pango / PANGO_SCALE;
+ if (horz_justify == 'r')
+ cairo_rel_move_to (cr, -width, 0);
+ else
+ cairo_rel_move_to (cr, -width / 2.0, 0);
+ }
+ if (vert_justify == 'x')
+ {
+ int baseline_pango = pango_layout_get_baseline (layout);
+ double baseline = (double) baseline_pango / PANGO_SCALE;
+ cairo_rel_move_to (cr, 0, -baseline);
+ }
+ else if (vert_justify != 't')
+ {
+ int height_pango;
+ double height;
+
+ pango_layout_get_size (layout, NULL, &height_pango);
+ height = (double) height_pango / PANGO_SCALE;
+ if (vert_justify == 'b')
+ cairo_rel_move_to (cr, 0, -height);
+ else if (vert_justify == 'c')
+ cairo_rel_move_to (cr, 0, -height / 2.0);
+ }
+ pango_cairo_show_layout (cr, layout);
+ g_object_unref (layout);
+
+ cairo_restore (cr);
+
+ cairo_new_path (cr);
+
+ pango_font_description_free (desc);
+}
/* Draw a tick mark at position
If label is non zero, then print it at the tick mark
*/
void
-draw_tick(struct chart *chart,
- enum tick_orientation orientation,
- double position,
- const char *label, ...)
+draw_tick (cairo_t *cr, const struct chart_geometry *geom,
+ enum tick_orientation orientation,
+ double position,
+ const char *label, ...)
{
const int tickSize = 10;
+ double x, y;
- assert(chart);
-
- pl_savestate_r(chart->lp);
+ cairo_move_to (cr, geom->data_left, geom->data_bottom);
- pl_move_r(chart->lp, chart->data_left, chart->data_bottom);
-
- if ( orientation == TICK_ABSCISSA )
- pl_flinerel_r(chart->lp, position, 0, position, -tickSize);
- else if (orientation == TICK_ORDINATE )
- pl_flinerel_r(chart->lp, 0, position, -tickSize, position);
+ if (orientation == TICK_ABSCISSA)
+ {
+ cairo_rel_move_to (cr, position, 0);
+ cairo_rel_line_to (cr, 0, -tickSize);
+ }
+ else if (orientation == TICK_ORDINATE)
+ {
+ cairo_rel_move_to (cr, 0, position);
+ cairo_rel_line_to (cr, -tickSize, 0);
+ }
else
NOT_REACHED ();
+ cairo_get_current_point (cr, &x, &y);
- if ( label ) {
- char buf[10];
- va_list ap;
- va_start(ap,label);
- vsnprintf(buf,10,label,ap);
+ cairo_stroke (cr);
- if ( orientation == TICK_ABSCISSA )
- pl_alabel_r(chart->lp, 'c','t', buf);
- else if (orientation == TICK_ORDINATE )
- {
- if ( fabs(position) < DBL_EPSILON )
- pl_moverel_r(chart->lp, 0, 10);
-
- pl_alabel_r(chart->lp, 'r','c', buf);
- }
-
- va_end(ap);
- }
-
- pl_restorestate_r(chart->lp);
+ if (label != NULL)
+ {
+ va_list ap;
+ char *s;
+
+ cairo_move_to (cr, x, y);
+
+ va_start (ap, label);
+ s = xvasprintf (label, ap);
+ if (orientation == TICK_ABSCISSA)
+ chart_label (cr, 'c', 't', geom->font_size, s);
+ else if (orientation == TICK_ORDINATE)
+ {
+ if (fabs (position) < DBL_EPSILON)
+ cairo_rel_move_to (cr, 0, 10);
+ chart_label (cr, 'r', 'c', geom->font_size, s);
+ }
+ free (s);
+ va_end (ap);
+ }
}
/* Write the title on a chart*/
void
-chart_write_title(struct chart *chart, const char *title, ...)
+chart_write_title (cairo_t *cr, const struct chart_geometry *geom,
+ const char *title, ...)
{
va_list ap;
- char buf[100];
-
- if ( ! chart )
- return ;
+ char *s;
- pl_savestate_r(chart->lp);
- pl_ffontsize_r(chart->lp,chart->font_size * 1.5);
- pl_move_r(chart->lp,chart->data_left, chart->title_bottom);
+ cairo_save (cr);
+ cairo_move_to (cr, geom->data_left, geom->title_bottom);
- va_start(ap,title);
- vsnprintf(buf,100,title,ap);
- pl_alabel_r(chart->lp,0,0,buf);
- va_end(ap);
+ va_start(ap, title);
+ s = xvasprintf (title, ap);
+ chart_label (cr, 'l', 'x', geom->font_size * 1.5, s);
+ free (s);
+ va_end (ap);
- pl_restorestate_r(chart->lp);
+ cairo_restore (cr);
}
/* Set the scale for the abscissa */
void
-chart_write_xscale(struct chart *ch, double min, double max, int ticks)
+chart_write_xscale (cairo_t *cr, struct chart_geometry *geom,
+ double min, double max, int ticks)
{
double x;
const double tick_interval =
- chart_rounded_tick( (max - min) / (double) ticks);
+ chart_rounded_tick ((max - min) / (double) ticks);
- assert ( ch );
-
-
- ch->x_max = ceil( max / tick_interval ) * tick_interval ;
- ch->x_min = floor ( min / tick_interval ) * tick_interval ;
-
-
- ch->abscissa_scale = fabs(ch->data_right - ch->data_left) /
- fabs(ch->x_max - ch->x_min);
-
- for(x = ch->x_min ; x <= ch->x_max; x += tick_interval )
- {
- draw_tick (ch, TICK_ABSCISSA,
- (x - ch->x_min) * ch->abscissa_scale, "%g", x);
- }
+ geom->x_max = ceil (max / tick_interval) * tick_interval;
+ geom->x_min = floor (min / tick_interval) * tick_interval;
+ geom->abscissa_scale = fabs(geom->data_right - geom->data_left) /
+ fabs(geom->x_max - geom->x_min);
+ for (x = geom->x_min; x <= geom->x_max; x += tick_interval)
+ draw_tick (cr, geom, TICK_ABSCISSA,
+ (x - geom->x_min) * geom->abscissa_scale, "%g", x);
}
/* Set the scale for the ordinate */
void
-chart_write_yscale(struct chart *ch, double smin, double smax, int ticks)
+chart_write_yscale (cairo_t *cr, struct chart_geometry *geom,
+ double smin, double smax, int ticks)
{
double y;
const double tick_interval =
- chart_rounded_tick( (smax - smin) / (double) ticks);
-
- if ( !ch )
- return;
+ chart_rounded_tick ((smax - smin) / (double) ticks);
- ch->y_max = ceil ( smax / tick_interval ) * tick_interval ;
- ch->y_min = floor ( smin / tick_interval ) * tick_interval ;
+ geom->y_max = ceil (smax / tick_interval) * tick_interval;
+ geom->y_min = floor (smin / tick_interval) * tick_interval;
- ch->ordinate_scale =
- fabs(ch->data_top - ch->data_bottom) / fabs(ch->y_max - ch->y_min) ;
+ geom->ordinate_scale =
+ (fabs (geom->data_top - geom->data_bottom)
+ / fabs (geom->y_max - geom->y_min));
- for(y = ch->y_min ; y <= ch->y_max; y += tick_interval )
- {
- draw_tick (ch, TICK_ORDINATE,
- (y - ch->y_min) * ch->ordinate_scale, "%g", y);
- }
+ for (y = geom->y_min; y <= geom->y_max; y += tick_interval)
+ draw_tick (cr, geom, TICK_ORDINATE,
+ (y - geom->y_min) * geom->ordinate_scale, "%g", y);
}
-
/* Write the abscissa label */
void
-chart_write_xlabel(struct chart *ch, const char *label)
+chart_write_xlabel (cairo_t *cr, const struct chart_geometry *geom,
+ const char *label)
{
- if ( ! ch )
- return ;
-
- pl_savestate_r(ch->lp);
-
- pl_move_r(ch->lp,ch->data_left, ch->abscissa_top);
- pl_alabel_r(ch->lp,0,'t',label);
-
- pl_restorestate_r(ch->lp);
-
+ cairo_move_to (cr, geom->data_left, geom->abscissa_top);
+ chart_label (cr, 'l', 't', geom->font_size, label);
}
-
-
/* Write the ordinate label */
void
-chart_write_ylabel(struct chart *ch, const char *label)
+chart_write_ylabel (cairo_t *cr, const struct chart_geometry *geom,
+ const char *label)
{
- if ( ! ch )
- return ;
-
- pl_savestate_r(ch->lp);
-
- pl_move_r(ch->lp, ch->data_bottom, ch->ordinate_right);
- pl_textangle_r(ch->lp, 90);
- pl_alabel_r(ch->lp, 0, 0, label);
-
- pl_restorestate_r(ch->lp);
+ cairo_save (cr);
+ cairo_translate (cr, -geom->data_bottom, -geom->ordinate_right);
+ cairo_move_to (cr, 0, 0);
+ cairo_rotate (cr, M_PI / 2.0);
+ chart_label (cr, 'l', 'x', geom->font_size, label);
+ cairo_restore (cr);
}
void
-chart_write_legend (struct chart *ch)
+chart_write_legend (cairo_t *cr, const struct chart_geometry *geom)
{
int i;
- const int vstep = ch->font_size * 2;
+ const int vstep = geom->font_size * 2;
const int xpad = 10;
const int ypad = 10;
const int swatch = 20;
- const int legend_top = ch->data_top;
+ const int legend_top = geom->data_top;
const int legend_bottom = legend_top -
- (vstep * ch->n_datasets + 2 * ypad );
-
- if ( ! ch )
- return ;
+ (vstep * geom->n_datasets + 2 * ypad );
- pl_savestate_r (ch->lp);
+ cairo_save (cr);
- pl_box_r (ch->lp, ch->legend_left, legend_top,
- ch->legend_right - xpad, legend_bottom);
+ cairo_rectangle (cr, geom->legend_left, legend_top,
+ geom->legend_right - xpad - geom->legend_left,
+ legend_bottom - legend_top);
+ cairo_stroke (cr);
- for (i = 0 ; i < ch->n_datasets ; ++i )
+ for (i = 0 ; i < geom->n_datasets ; ++i )
{
- const int ypos = vstep * (i + 1);
- const int xpos = ch->legend_left + xpad;
- pl_move_r (ch->lp, xpos, legend_top - ypos);
-
- pl_savestate_r(ch->lp);
- pl_fillcolorname_r (ch->lp, data_colour [ i % N_CHART_COLOURS]);
-
- pl_pentype_r (ch->lp, 1);
- pl_filltype_r (ch->lp, 1);
- pl_boxrel_r (ch->lp, 0, 0, swatch, swatch);
-
-
- pl_moverel_r (ch->lp, swatch, 0);
- pl_alabel_r (ch->lp, 0, 0, ch->dataset[i]);
-
- pl_restorestate_r (ch->lp);
+ const int ypos = legend_top - vstep * (i + 1);
+ const int xpos = geom->legend_left + xpad;
+ const struct chart_colour *colour;
+
+ cairo_move_to (cr, xpos, ypos);
+
+ cairo_save (cr);
+ colour = &data_colour [ i % N_CHART_COLOURS];
+ cairo_set_source_rgb (cr,
+ colour->red / 255.0,
+ colour->green / 255.0,
+ colour->blue / 255.0);
+ cairo_rectangle (cr, xpos, ypos, swatch, swatch);
+ cairo_fill_preserve (cr);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+
+ cairo_move_to (cr, xpos + swatch * 1.5, ypos);
+ chart_label (cr, 'l', 'x', geom->font_size, geom->dataset[i]);
}
- pl_restorestate_r (ch->lp);
+ cairo_restore (cr);
}
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+#ifndef PLOT_CHART_H
+#define PLOT_CHART_H
+
+#include <cairo/cairo.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <math/chart-geometry.h>
#include <output/chart.h>
+#include <output/chart-provider.h>
#include <libpspp/compiler.h>
#include <libpspp/str.h>
#include <output/manager.h>
#include <output/output.h>
-#include "xalloc.h"
-
-#ifndef PLOT_CHART_H
-#define PLOT_CHART_H
-
#define N_CHART_COLOURS 9
-extern const char *const data_colour[];
+extern const struct chart_colour data_colour[];
enum tick_orientation
{
TICK_ORDINATE
};
+struct chart_geometry;
+
+
+enum marker_type
+ {
+ MARKER_CIRCLE, /* Hollow circle. */
+ MARKER_ASTERISK, /* Asterisk (*). */
+ MARKER_SQUARE /* Hollow square. */
+ };
+
+void chart_draw_marker (cairo_t *, double x, double y, enum marker_type,
+ double size);
+
+void chart_label (cairo_t *, int horz_justify, int vert_justify,
+ double font_size, const char *);
/* Draw a tick mark at position
If label is non zero, then print it at the tick mark
*/
-void draw_tick(struct chart *chart,
+void draw_tick(cairo_t *, const struct chart_geometry *,
enum tick_orientation orientation,
double position,
const char *label, ...)
- PRINTF_FORMAT (4, 5);
+ PRINTF_FORMAT (5, 6);
/* Write the title on a chart*/
-void chart_write_title(struct chart *chart, const char *title, ...)
- PRINTF_FORMAT (2, 3);
+void chart_write_title(cairo_t *, const struct chart_geometry *,
+ const char *title, ...)
+ PRINTF_FORMAT (3, 4);
/* Set the scale for the abscissa */
-void chart_write_xscale(struct chart *ch, double min, double max, int ticks);
+void chart_write_xscale(cairo_t *, struct chart_geometry *,
+ double min, double max, int ticks);
/* Set the scale for the ordinate */
-void chart_write_yscale(struct chart *ch, double smin, double smax, int ticks);
+void chart_write_yscale(cairo_t *, struct chart_geometry *,
+ double smin, double smax, int ticks);
-void chart_write_xlabel(struct chart *ch, const char *label) ;
+void chart_write_xlabel(cairo_t *, const struct chart_geometry *,
+ const char *label) ;
/* Write the ordinate label */
-void chart_write_ylabel(struct chart *ch, const char *label);
+void chart_write_ylabel(cairo_t *, const struct chart_geometry *,
+ const char *label);
-void chart_write_legend (struct chart *ch);
+void chart_write_legend (cairo_t *, const struct chart_geometry *);
#endif
#include <config.h>
#include <stdio.h>
-#include <plot.h>
#include <math.h>
#include <gsl/gsl_histogram.h>
#include <gsl/gsl_randist.h>
#include <output/charts/plot-hist.h>
#include <output/charts/plot-chart.h>
+#include <output/chart-provider.h>
#include <data/variable.h>
+#include <libpspp/cast.h>
#include <libpspp/hash.h>
#include <output/chart.h>
#include <math/histogram.h>
#include "gettext.h"
#define _(msgid) gettext (msgid)
+static const struct chart_class histogram_chart_class;
+
/* Write the legend of the chart */
static void
-histogram_write_legend (struct chart *ch, double n, double mean, double stddev)
+histogram_write_legend (cairo_t *cr, const struct chart_geometry *geom,
+ double n, double mean, double stddev)
{
- char buf[100];
-
- if (!ch)
- return ;
-
- pl_savestate_r (ch->lp);
+ double y = geom->data_bottom;
+ cairo_save (cr);
- sprintf (buf, "N = %.2f", n);
- pl_move_r (ch->lp, ch->legend_left, ch->data_bottom);
- pl_alabel_r (ch->lp, 0, 'b', buf);
+ if (n != SYSMIS)
+ {
+ char *buf = xasprintf ("N = %.2f", n);
+ cairo_move_to (cr, geom->legend_left, y);
+ chart_label (cr, 'l', 'b', geom->font_size, buf);
+ y += geom->font_size * 1.5;
+ free (buf);
+ }
- sprintf (buf, "Mean = %.1f", mean);
- pl_fmove_r (ch->lp,ch->legend_left,ch->data_bottom + ch->font_size * 1.5);
- pl_alabel_r (ch->lp, 0, 'b', buf);
+ if (mean != SYSMIS)
+ {
+ char *buf = xasprintf ("Mean = %.1f", mean);
+ cairo_move_to (cr,geom->legend_left, y);
+ chart_label (cr, 'l', 'b', geom->font_size, buf);
+ y += geom->font_size * 1.5;
+ free (buf);
+ }
- sprintf (buf, "Std. Dev = %.2f", stddev);
- pl_fmove_r (ch->lp, ch->legend_left, ch->data_bottom + ch->font_size * 1.5 * 2);
- pl_alabel_r (ch->lp, 0, 'b', buf);
+ if (stddev != SYSMIS)
+ {
+ char *buf = xasprintf ("Std. Dev = %.2f", stddev);
+ cairo_move_to (cr, geom->legend_left, y);
+ chart_label (cr, 'l', 'b', geom->font_size, buf);
+ free (buf);
+ }
- pl_restorestate_r (ch->lp);
+ cairo_restore (cr);
}
-static void hist_draw_bar (struct chart *ch, const struct histogram *hist, int bar);
-
-
static void
-hist_draw_bar (struct chart *ch, const struct histogram *hist, int bar)
+hist_draw_bar (cairo_t *cr, const struct chart_geometry *geom,
+ const gsl_histogram *h, int bar)
{
- if (!ch)
- return ;
-
- {
- double upper;
- double lower;
- double height;
-
- const size_t bins = gsl_histogram_bins (hist->gsl_hist);
- const double x_pos = (ch->data_right - ch->data_left) * bar / (double) bins ;
- const double width = (ch->data_right - ch->data_left) / (double) bins ;
-
- assert ( 0 == gsl_histogram_get_range (hist->gsl_hist, bar, &lower, &upper));
-
- assert ( upper >= lower);
-
- height = gsl_histogram_get (hist->gsl_hist, bar) *
- (ch->data_top - ch->data_bottom) / gsl_histogram_max_val (hist->gsl_hist);
-
- pl_savestate_r (ch->lp);
- pl_move_r (ch->lp,ch->data_left, ch->data_bottom);
- pl_fillcolorname_r (ch->lp, ch->fill_colour);
- pl_filltype_r (ch->lp,1);
-
-
- pl_fboxrel_r (ch->lp,
- x_pos, 0,
- x_pos + width, height);
-
- pl_restorestate_r (ch->lp);
-
- draw_tick (ch, TICK_ABSCISSA,
- x_pos + width / 2.0, "%g", (upper + lower) / 2.0);
- }
+ double upper;
+ double lower;
+ double height;
+
+ const size_t bins = gsl_histogram_bins (h);
+ const double x_pos = (geom->data_right - geom->data_left) * bar / (double) bins ;
+ const double width = (geom->data_right - geom->data_left) / (double) bins ;
+
+ assert ( 0 == gsl_histogram_get_range (h, bar, &lower, &upper));
+
+ assert ( upper >= lower);
+
+ height = gsl_histogram_get (h, bar) *
+ (geom->data_top - geom->data_bottom) / gsl_histogram_max_val (h);
+
+ cairo_rectangle (cr, geom->data_left + x_pos, geom->data_bottom,
+ width, height);
+ cairo_save (cr);
+ cairo_set_source_rgb (cr,
+ geom->fill_colour.red / 255.0,
+ geom->fill_colour.green / 255.0,
+ geom->fill_colour.blue / 255.0);
+ cairo_fill_preserve (cr);
+ cairo_restore (cr);
+ cairo_stroke (cr);
+
+ draw_tick (cr, geom, TICK_ABSCISSA,
+ x_pos + width / 2.0, "%g", (upper + lower) / 2.0);
}
-
-
-void
-histogram_plot (const struct histogram *hist,
- const char *label,
- const struct moments1 *m)
+struct histogram_chart
+ {
+ struct chart chart;
+ gsl_histogram *gsl_hist;
+ char *label;
+ double n;
+ double mean;
+ double stddev;
+ bool show_normal;
+ };
+
+/* Plots a histogram of the data in HIST with the given LABEL.
+ Labels the histogram with each of N, MEAN, and STDDEV that is
+ not SYSMIS. If all three are not SYSMIS and SHOW_NORMAL is
+ true, also draws a normal curve on the histogram. */
+struct chart *
+histogram_chart_create (const struct histogram *hist, const char *label,
+ double n, double mean, double stddev,
+ bool show_normal)
{
- double mean, var, n;
-
- moments1_calculate (m, &n, &mean, &var, NULL, NULL);
-
- histogram_plot_n (hist, label, n, mean, sqrt(var), m);
+ struct histogram_chart *h;
+
+ h = xmalloc (sizeof *h);
+ chart_init (&h->chart, &histogram_chart_class);
+ h->gsl_hist = hist->gsl_hist ? gsl_histogram_clone (hist->gsl_hist) : NULL;
+ h->label = xstrdup (label);
+ h->n = n;
+ h->mean = mean;
+ h->stddev = stddev;
+ h->show_normal = show_normal;
+ return &h->chart;
}
-
-/* This function is deprecated. Don't use it in new code */
-void
-histogram_plot_n (const struct histogram *hist,
- const char *label,
- double n, double mean, double stddev,
- bool show_normal)
+static void
+histogram_chart_draw (const struct chart *chart, cairo_t *cr,
+ struct chart_geometry *geom)
{
+ struct histogram_chart *h = UP_CAST (chart, struct histogram_chart, chart);
int i;
int bins;
- struct chart *ch = chart_create ();
-
- chart_write_title (ch, _("HISTOGRAM"));
+ chart_write_title (cr, geom, _("HISTOGRAM"));
- chart_write_ylabel (ch, _("Frequency"));
- chart_write_xlabel (ch, label);
+ chart_write_ylabel (cr, geom, _("Frequency"));
+ chart_write_xlabel (cr, geom, h->label);
- if ( ! hist ) /* If this happens, probably all values are SYSMIS */
+ if (h->gsl_hist == NULL)
{
- chart_submit (ch);
+ /* Probably all values are SYSMIS. */
return;
}
- else
- {
- bins = gsl_histogram_bins (hist->gsl_hist);
- }
- chart_write_yscale (ch, 0, gsl_histogram_max_val (hist->gsl_hist), 5);
+ bins = gsl_histogram_bins (h->gsl_hist);
+
+ chart_write_yscale (cr, geom, 0, gsl_histogram_max_val (h->gsl_hist), 5);
- for ( i = 0 ; i < bins ; ++i )
- hist_draw_bar (ch, hist, i);
+ for (i = 0; i < bins; i++)
+ hist_draw_bar (cr, geom, h->gsl_hist, i);
- histogram_write_legend (ch, n, mean, stddev);
+ histogram_write_legend (cr, geom, h->n, h->mean, h->stddev);
- if (show_normal)
+ if (h->show_normal
+ && h->n != SYSMIS && h->mean != SYSMIS && h->stddev != SYSMIS)
{
/* Draw the normal curve */
+ double d;
+ double x_min, x_max, not_used;
+ double abscissa_scale;
+ double ordinate_scale;
+ double range;
- double d ;
- double x_min, x_max, not_used ;
- double abscissa_scale ;
- double ordinate_scale ;
- double range ;
-
- gsl_histogram_get_range (hist->gsl_hist, 0, &x_min, ¬_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
+ };
/* PSPP - a program for statistical analysis.
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
-#ifndef PLOT_HIST_H
-#define PLOT_HIST_H
+#ifndef OUTPUT_PLOT_HIST_H
+#define OUTPUT_PLOT_HIST_H
#include <stdbool.h>
struct chart;
-struct moments1;
struct histogram;
-/* Plot M onto histogram HIST and label it with LABEL */
-void histogram_plot (const struct histogram *hist,
- const char *label, const struct moments1 *m);
-
-
-/* A wrapper aroud histogram_plot.
- Don't use this function. It's legacy only */
-void histogram_plot_n (const struct histogram *hist,
- const char *label,
- double n, double mean, double var,
- bool show_normal);
-
-
-#endif
+/* Creates and returns a new chart that depicts a histogram of
+ the data in HIST with the given LABEL. Labels the histogram
+ with each of N, MEAN, and STDDEV that is not SYSMIS. If all
+ three are not SYSMIS and SHOW_NORMAL is true, also draws a
+ normal curve on the histogram. */
+struct chart *histogram_chart_create (const struct histogram *hist,
+ const char *label,
+ double n, double mean, double stddev,
+ bool show_normal);
+
+#endif /* output/plot-hist.h */
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/charts/roc-chart.h>
+
+#include <output/chart-provider.h>
+#include <output/charts/cartesian.h>
+#include <output/charts/plot-chart.h>
+#include <data/casereader.h>
+#include <language/stats/roc.h>
+
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+struct roc_var
+ {
+ char *name;
+ struct casereader *cutpoint_reader;
+ };
+
+struct roc_chart
+ {
+ struct chart chart;
+ bool reference;
+ struct roc_var *vars;
+ size_t n_vars;
+ size_t allocated_vars;
+ };
+
+static const struct chart_class roc_chart_class;
+
+struct roc_chart *
+roc_chart_create (bool reference)
+{
+ struct roc_chart *rc = xmalloc (sizeof *rc);
+ chart_init (&rc->chart, &roc_chart_class);
+ rc->reference = reference;
+ rc->vars = NULL;
+ rc->n_vars = 0;
+ rc->allocated_vars = 0;
+ return rc;
+}
+
+void
+roc_chart_add_var (struct roc_chart *rc, const char *var_name,
+ const struct casereader *cutpoint_reader)
+{
+ struct roc_var *rv;
+
+ if (rc->n_vars >= rc->allocated_vars)
+ rc->vars = x2nrealloc (rc->vars, &rc->allocated_vars, sizeof *rc->vars);
+
+ rv = &rc->vars[rc->n_vars++];
+ rv->name = xstrdup (var_name);
+ rv->cutpoint_reader = casereader_clone (cutpoint_reader);
+}
+
+struct chart *
+roc_chart_get_chart (struct roc_chart *rc)
+{
+ return &rc->chart;
+}
+
+static void
+roc_chart_draw (const struct chart *chart, cairo_t *cr,
+ struct chart_geometry *geom)
+{
+ const struct roc_chart *rc = UP_CAST (chart, struct roc_chart, chart);
+ size_t i;
+
+ chart_write_title (cr, geom, _("ROC Curve"));
+ chart_write_xlabel (cr, geom, _("1 - Specificity"));
+ chart_write_ylabel (cr, geom, _("Sensitivity"));
+
+ chart_write_xscale (cr, geom, 0, 1, 5);
+ chart_write_yscale (cr, geom, 0, 1, 5);
+
+ if ( rc->reference )
+ {
+ chart_line (cr, geom, 1.0, 0,
+ 0.0, 1.0,
+ CHART_DIM_X);
+ }
+
+ for (i = 0; i < rc->n_vars; ++i)
+ {
+ const struct roc_var *rv = &rc->vars[i];
+ struct casereader *r = casereader_clone (rv->cutpoint_reader);
+ struct ccase *cc;
+
+ chart_vector_start (cr, geom, rv->name);
+ for (; (cc = casereader_read (r)) != NULL; case_unref (cc))
+ {
+ double se = case_data_idx (cc, ROC_TP)->f;
+ double sp = case_data_idx (cc, ROC_TN)->f;
+
+ se /= case_data_idx (cc, ROC_FN)->f + case_data_idx (cc, ROC_TP)->f ;
+ sp /= case_data_idx (cc, ROC_TN)->f + case_data_idx (cc, ROC_FP)->f ;
+
+ chart_vector (cr, geom, 1 - sp, se);
+ }
+ chart_vector_end (cr, geom);
+ casereader_destroy (r);
+ }
+
+ chart_write_legend (cr, geom);
+}
+
+static void
+roc_chart_destroy (struct chart *chart)
+{
+ struct roc_chart *rc = UP_CAST (chart, struct roc_chart, chart);
+ size_t i;
+
+ for (i = 0; i < rc->n_vars; i++)
+ {
+ struct roc_var *rv = &rc->vars[i];
+ free (rv->name);
+ casereader_destroy (rv->cutpoint_reader);
+ }
+ free (rc->vars);
+ free (rc);
+}
+
+static const struct chart_class roc_chart_class =
+ {
+ roc_chart_draw,
+ roc_chart_destroy
+ };
+
+
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CHARTS_ROC_CHART_H
+#define OUTPUT_CHARTS_ROC_CHART_H 1
+
+#include <stdbool.h>
+
+struct casereader;
+
+struct roc_chart *roc_chart_create (bool reference);
+void roc_chart_add_var (struct roc_chart *, const char *var_name,
+ const struct casereader *cutpoint_reader);
+struct chart *roc_chart_get_chart (struct roc_chart *);
+
+#endif /* output/charts/roc-chart.h */
/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <libpspp/assertion.h>
#include <libpspp/compiler.h>
#include <data/file-name.h>
-#include "error.h"
-#include "output.h"
-#include "manager.h"
-#include "table.h"
+#include <output/chart-provider.h>
+#include <output/output.h>
+#include <output/manager.h>
+#include <output/table.h>
#include <libpspp/version.h>
+#include "error.h"
#include "xalloc.h"
#include "gettext.h"
static void escape_string (FILE *file,
const char *text, size_t length,
const char *space);
-static bool handle_option (struct outp_driver *this,
+static bool handle_option (void *this,
const char *key, const struct string *val);
static void print_title_tag (FILE *file, const char *name,
const char *content);
static bool
-html_open_driver (struct outp_driver *this, struct substring options)
+html_open_driver (const char *name, int types, struct substring options)
{
+ struct outp_driver *this;
struct html_driver_ext *x;
+ this = outp_allocate_driver (&html_class, name, types);
this->ext = x = xmalloc (sizeof *x);
x->file_name = xstrdup ("pspp.html");
x->chart_file_name = xstrdup ("pspp-#.png");
x->file = NULL;
- x->chart_cnt = 0;
+ x->chart_cnt = 1;
- outp_parse_options (options, handle_option, this);
+ outp_parse_options (name, options, handle_option, this);
x->file = fn_open (x->file_name, "w");
if (x->file == NULL)
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;
}
return ok;
}
-/* Link the image contained in FILE_NAME to the
- HTML stream in FILE. */
-static void
-link_image (FILE *file, char *file_name)
-{
- fprintf (file, "<IMG SRC=\"%s\"/>", file_name);
- }
-
/* Generic option types. */
enum
{
};
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;
static void output_tab_table (struct outp_driver *, struct tab_table *);
+static void
+html_output_chart (struct outp_driver *this, const struct chart *chart)
+{
+ struct html_driver_ext *x = this->ext;
+ char *file_name;
+
+ file_name = chart_draw_png (chart, x->chart_file_name, x->chart_cnt++);
+ fprintf (x->file, "<IMG SRC=\"%s\"/>", file_name);
+ free (file_name);
+}
+
static void
html_submit (struct outp_driver *this, struct som_entity *s)
{
extern struct som_table_class tab_table_class;
- struct html_driver_ext *x = this->ext;
assert (s->class == &tab_table_class ) ;
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 ();
}
{
struct html_driver_ext *x = this->ext;
- if (t->nr == 1 && t->nc == 1)
+ if (tab_nr (t) == 1 && tab_nc (t) == 1)
{
fputs ("<P>", x->file);
html_put_cell_contents (this, t->ct[0], *t->cc);
int r;
unsigned char *ct = t->ct;
- for (r = 0; r < t->nr; r++)
+ for (r = 0; r < tab_nr (t); r++)
{
int c;
fputs (" <TR>\n", x->file);
- for (c = 0; c < t->nc; c++, ct++)
+ for (c = 0; c < tab_nc (t); c++, ct++)
{
struct substring *cc;
const char *tag;
struct tab_joined_cell *j = NULL;
- cc = t->cc + c + r * t->nc;
+ cc = t->cc + c + r * tab_nc (t);
if (*ct & TAB_JOIN)
{
j = (struct tab_joined_cell *) ss_data (*cc);
}
/* Output <TD> or <TH> tag. */
- tag = (r < t->t || r >= t->nr - t->b
- || c < t->l || c >= t->nc - t->r) ? "TH" : "TD";
+ tag = (r < tab_t (t) || r >= tab_nr (t) - tab_b (t)
+ || c < tab_l (t) || c >= tab_nc (t) - tab_r (t)) ? "TH" : "TD";
fprintf (x->file, " <%s ALIGN=%s",
tag,
(*ct & TAB_ALIGN_MASK) == TAB_LEFT ? "LEFT"
fputs ("</TABLE>\n\n", x->file);
}
-static void
-html_initialise_chart (struct outp_driver *this UNUSED, struct chart *ch)
-{
- struct html_driver_ext *x = this->ext;
- chart_init_separate (ch, "png", x->chart_file_name, ++x->chart_cnt);
-}
-
-static void
-html_finalise_chart(struct outp_driver *d UNUSED, struct chart *ch)
-{
- chart_finalise_separate (ch);
-}
-
/* HTML driver class. */
NULL,
NULL,
+ html_output_chart,
+
html_submit,
NULL,
NULL,
NULL,
- html_initialise_chart,
- html_finalise_chart
};
/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
-#include "manager.h"
+
+#include <output/manager.h>
+
#include <stdio.h>
#include <stdlib.h>
+
#include <libpspp/assertion.h>
-#include "output.h"
+#include <output/output.h>
+
+#include "gl/xalloc.h"
/* Table. */
-int table_num = 1;
-int subtable_num;
+static int table_num = 1;
+static int subtable_num;
+
+/* Name of PSPP's current command, or NULL if outside a command. */
+static char *command_name;
\f
+struct som_entity *
+som_entity_clone (struct som_entity *entity)
+{
+ struct som_entity *copy = xmemdup (entity, sizeof *entity);
+ copy->command_name = xstrdup (entity->command_name);
+ return copy;
+}
+
+void
+som_entity_destroy (struct som_entity *entity)
+{
+ if (entity != NULL)
+ {
+ free (entity->command_name);
+ free (entity);
+ }
+}
+
/* Increments table_num so different procedures' output can be
distinguished. */
void
}
}
+/* 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)
d->cp_y += d->font_height;
}
\f
-/* Driver. */
-static struct outp_driver *d = 0;
-
-/* Table. */
-static struct som_entity *t = 0;
-
-/* Flags. */
-static unsigned flags;
-
-/* Number of columns, rows. */
-static int nc, nr;
-
-/* Number of columns or rows in left, right, top, bottom headers. */
-static int hl, hr, ht, hb;
-
-/* Column style. */
-static int cs;
-
-/* Table height, width. */
-static int th, tw;
-
-static void render_columns (void);
-static void render_simple (void);
-static void render_segments (void);
+static void render_columns (void *r, struct outp_driver *, struct som_entity *,
+ int tw, int th,
+ int hl, int hr, int ht, int hb);
+static void render_simple (void *r, struct outp_driver *, struct som_entity *,
+ int tw, int th,
+ int hl, int hr, int ht, int hb);
+static void render_segments (void *r, struct outp_driver *,
+ struct som_entity *,
+ int tw, int th,
+ int hl, int hr, int ht, int hb);
static void output_entity (struct outp_driver *, struct som_entity *);
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;
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);
{
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)
{
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)
/* 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++;
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++;
}
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;
}
/* 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
enum som_type
{
- SOM_TABLE,
- SOM_CHART
+ SOM_TABLE
} ;
/* Entity (Table or Chart) . */
{
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
{
/* Cumulation types. */
enum
{
- SOM_ROWS, SOM_ROW = SOM_ROWS, /* Rows. */
- SOM_COLUMNS, SOM_COLUMN = SOM_COLUMNS /* Columns. */
+ SOM_ROWS, /* Rows. */
+ SOM_COLUMNS /* Columns. */
};
/* Flags. */
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. */
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* A driver for creating OpenDocument Format text files from PSPP's output */
+
+#include <libpspp/assertion.h>
+#include <libpspp/version.h>
+
+#include <output/manager.h>
+#include <output/output.h>
+#include <output/table.h>
+
+#include <time.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <libgen.h>
+
+#include <libxml/xmlwriter.h>
+
+#include "xalloc.h"
+
+#include "error.h"
+
+#define _xml(X) (const xmlChar *)(X)
+
+
+struct odf_driver_options
+{
+ struct outp_driver *driver;
+
+ char *file_name; /* Output file name. */
+ bool debug;
+};
+
+
+struct odt_driver_ext
+{
+ /* The name of the temporary directory used to construct the ODF */
+ char *dirname;
+
+ /* Writer for the content.xml file */
+ xmlTextWriterPtr content_wtr;
+
+ /* Writer fot the manifest.xml file */
+ xmlTextWriterPtr manifest_wtr;
+
+ struct odf_driver_options opts;
+};
+
+
+
+/* Create the "mimetype" file needed by ODF */
+static void
+create_mimetype (const char *dirname)
+{
+ FILE *fp;
+ struct string filename;
+ ds_init_cstr (&filename, dirname);
+ ds_put_cstr (&filename, "/mimetype");
+ fp = fopen (ds_cstr (&filename), "w");
+ ds_destroy (&filename);
+
+ assert (fp);
+ fprintf (fp, "application/vnd.oasis.opendocument.text");
+ fclose (fp);
+}
+
+/* Create a new XML file called FILENAME in the temp directory, and return a writer for it */
+static xmlTextWriterPtr
+create_writer (const struct odt_driver_ext *driver, const char *filename)
+{
+ char *copy = NULL;
+ xmlTextWriterPtr w;
+ struct string str;
+ ds_init_cstr (&str, driver->dirname);
+ ds_put_cstr (&str, "/");
+ ds_put_cstr (&str, filename);
+
+ /* dirname modifies its argument, so we must copy it */
+ copy = xstrdup (ds_cstr (&str));
+ mkdir (dirname (copy), 0700);
+ free (copy);
+
+ w = xmlNewTextWriterFilename (ds_cstr (&str), 0);
+
+ ds_destroy (&str);
+
+ xmlTextWriterStartDocument (w, NULL, "UTF-8", NULL);
+
+ return w;
+}
+
+
+static void
+register_file (struct odt_driver_ext *x, const char *filename)
+{
+ assert (x->manifest_wtr);
+ xmlTextWriterStartElement (x->manifest_wtr, _xml("manifest:file-entry"));
+ xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:media-type"), _xml("text/xml"));
+ xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:full-path"), _xml (filename));
+ xmlTextWriterEndElement (x->manifest_wtr);
+}
+
+static void
+write_style_data (struct odt_driver_ext *x)
+{
+ xmlTextWriterPtr w = create_writer (x, "styles.xml");
+ register_file (x, "styles.xml");
+
+ xmlTextWriterStartElement (w, _xml ("office:document-styles"));
+ xmlTextWriterWriteAttribute (w, _xml ("xmlns:office"),
+ _xml ("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+
+ xmlTextWriterWriteAttribute (w, _xml ("xmlns:style"),
+ _xml ("urn:oasis:names:tc:opendocument:xmlns:style:1.0"));
+
+ xmlTextWriterWriteAttribute (w, _xml ("xmlns:fo"),
+ _xml ("urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0") );
+
+ xmlTextWriterWriteAttribute (w, _xml ("office:version"), _xml ("1.1"));
+
+
+
+ xmlTextWriterStartElement (w, _xml ("office:styles"));
+
+
+ {
+ xmlTextWriterStartElement (w, _xml ("style:style"));
+ xmlTextWriterWriteAttribute (w, _xml ("style:name"),
+ _xml ("Standard"));
+
+ xmlTextWriterWriteAttribute (w, _xml ("style:family"),
+ _xml ("paragraph"));
+
+ xmlTextWriterWriteAttribute (w, _xml ("style:class"),
+ _xml ("text"));
+
+ xmlTextWriterEndElement (w); /* style:style */
+ }
+
+ {
+ xmlTextWriterStartElement (w, _xml ("style:style"));
+ xmlTextWriterWriteAttribute (w, _xml ("style:name"),
+ _xml ("Table_20_Contents"));
+
+ xmlTextWriterWriteAttribute (w, _xml ("style:display-name"),
+ _xml ("Table Contents"));
+
+ xmlTextWriterWriteAttribute (w, _xml ("style:family"),
+ _xml ("paragraph"));
+
+ xmlTextWriterWriteAttribute (w, _xml ("style:parent-style-name"),
+ _xml ("Standard"));
+
+ xmlTextWriterWriteAttribute (w, _xml ("style:class"),
+ _xml ("extra"));
+
+ xmlTextWriterEndElement (w); /* style:style */
+ }
+
+ {
+ xmlTextWriterStartElement (w, _xml ("style:style"));
+ xmlTextWriterWriteAttribute (w, _xml ("style:name"),
+ _xml ("Table_20_Heading"));
+
+ xmlTextWriterWriteAttribute (w, _xml ("style:display-name"),
+ _xml ("Table Heading"));
+
+ xmlTextWriterWriteAttribute (w, _xml ("style:family"),
+ _xml ("paragraph"));
+
+ xmlTextWriterWriteAttribute (w, _xml ("style:parent-style-name"),
+ _xml ("Table_20_Contents"));
+
+ xmlTextWriterWriteAttribute (w, _xml ("style:class"),
+ _xml ("extra"));
+
+
+ xmlTextWriterStartElement (w, _xml ("style:text-properties"));
+ xmlTextWriterWriteAttribute (w, _xml ("fo:font-weight"), _xml ("bold"));
+ xmlTextWriterWriteAttribute (w, _xml ("style:font-weight-asian"), _xml ("bold"));
+ xmlTextWriterWriteAttribute (w, _xml ("style:font-weight-complex"), _xml ("bold"));
+ xmlTextWriterEndElement (w); /* style:text-properties */
+
+ xmlTextWriterEndElement (w); /* style:style */
+ }
+
+
+ xmlTextWriterEndElement (w); /* office:styles */
+ xmlTextWriterEndElement (w); /* office:document-styles */
+
+ xmlTextWriterEndDocument (w);
+ xmlFreeTextWriter (w);
+}
+
+static void
+write_meta_data (struct odt_driver_ext *x)
+{
+ xmlTextWriterPtr w = create_writer (x, "meta.xml");
+ register_file (x, "meta.xml");
+
+ xmlTextWriterStartElement (w, _xml ("office:document-meta"));
+ xmlTextWriterWriteAttribute (w, _xml ("xmlns:office"), _xml ("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+ xmlTextWriterWriteAttribute (w, _xml ("xmlns:dc"), _xml ("http://purl.org/dc/elements/1.1/"));
+ xmlTextWriterWriteAttribute (w, _xml ("xmlns:meta"), _xml ("urn:oasis:names:tc:opendocument:xmlns:meta:1.0"));
+ xmlTextWriterWriteAttribute (w, _xml ("xmlns:ooo"), _xml("http://openoffice.org/2004/office"));
+ xmlTextWriterWriteAttribute (w, _xml ("office:version"), _xml("1.1"));
+
+ xmlTextWriterStartElement (w, _xml ("office:meta"));
+ {
+ xmlTextWriterStartElement (w, _xml ("meta:generator"));
+ xmlTextWriterWriteString (w, _xml (stat_version));
+ xmlTextWriterEndElement (w);
+ }
+
+
+ {
+ char buf[30];
+ struct passwd *pw = getpwuid (getuid ());
+ time_t t = time (NULL);
+ struct tm *tm = localtime (&t);
+
+ strftime (buf, 30, "%Y-%m-%dT%H:%M:%S", tm);
+
+ xmlTextWriterStartElement (w, _xml ("meta:initial-creator"));
+ xmlTextWriterWriteString (w, _xml (strtok (pw->pw_gecos, ",")));
+ xmlTextWriterEndElement (w);
+
+ xmlTextWriterStartElement (w, _xml ("meta:creation-date"));
+ xmlTextWriterWriteString (w, _xml (buf));
+ xmlTextWriterEndElement (w);
+
+ xmlTextWriterStartElement (w, _xml ("dc:creator"));
+ xmlTextWriterWriteString (w, _xml (strtok (pw->pw_gecos, ",")));
+
+ xmlTextWriterEndElement (w);
+
+ xmlTextWriterStartElement (w, _xml ("dc:date"));
+ xmlTextWriterWriteString (w, _xml (buf));
+ xmlTextWriterEndElement (w);
+ }
+
+ xmlTextWriterEndElement (w);
+ xmlTextWriterEndElement (w);
+ xmlTextWriterEndDocument (w);
+ xmlFreeTextWriter (w);
+}
+
+enum
+{
+ output_file_arg,
+ boolean_arg,
+};
+
+static const struct outp_option option_tab[] =
+{
+ {"output-file", output_file_arg,0},
+
+ {"debug", boolean_arg, 1},
+
+ {NULL, 0, 0},
+};
+
+static bool
+handle_option (void *options_, const char *key, const struct string *val)
+{
+ struct odf_driver_options *options = options_;
+ struct outp_driver *this = options->driver;
+ int subcat;
+ char *value = ds_cstr (val);
+
+ switch (outp_match_keyword (key, option_tab, &subcat))
+ {
+ case -1:
+ error (0, 0,
+ _("unknown configuration parameter `%s' for %s device "
+ "driver"), key, this->class->name);
+ break;
+ case output_file_arg:
+ free (options->file_name);
+ options->file_name = xstrdup (value);
+ break;
+ case boolean_arg:
+ if (!strcmp (value, "on") || !strcmp (value, "true")
+ || !strcmp (value, "yes") || atoi (value))
+ options->debug = true;
+ else if (!strcmp (value, "off") || !strcmp (value, "false")
+ || !strcmp (value, "no") || !strcmp (value, "0"))
+ options->debug = false;
+ else
+ {
+ error (0, 0, _("boolean value expected for %s"), key);
+ return false;
+ }
+ break;
+
+ default:
+ NOT_REACHED ();
+ }
+
+ return true;
+}
+
+
+static bool
+odt_open_driver (const char *name, int types, struct substring option_string)
+{
+ struct odt_driver_ext *x;
+ struct outp_driver *this = outp_allocate_driver (&odt_class, name, types);
+
+ this->ext = x = xmalloc (sizeof *x);
+
+ x->opts.driver = this;
+ x->opts.file_name = xstrdup ("pspp.pdt");
+ x->opts.debug = false;
+
+ outp_parse_options (this->name, option_string, handle_option, &x->opts);
+
+ outp_register_driver (this);
+
+ x->dirname = xstrdup ("odt-XXXXXX");
+ mkdtemp (x->dirname);
+
+ create_mimetype (x->dirname);
+
+ /* Create the manifest */
+ x->manifest_wtr = create_writer (x, "META-INF/manifest.xml");
+
+ xmlTextWriterStartElement (x->manifest_wtr, _xml("manifest:manifest"));
+ xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("xmlns:manifest"),
+ _xml("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"));
+
+
+ /* Add a manifest entry for the document as a whole */
+ xmlTextWriterStartElement (x->manifest_wtr, _xml("manifest:file-entry"));
+ xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:media-type"), _xml("application/vnd.oasis.opendocument.text"));
+ xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:full-path"), _xml("/"));
+ xmlTextWriterEndElement (x->manifest_wtr);
+
+
+ write_meta_data (x);
+ write_style_data (x);
+
+ x->content_wtr = create_writer (x, "content.xml");
+ register_file (x, "content.xml");
+
+
+ /* Some necessary junk at the start */
+ xmlTextWriterStartElement (x->content_wtr, _xml("office:document-content"));
+ xmlTextWriterWriteAttribute (x->content_wtr, _xml("xmlns:office"),
+ _xml("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
+
+ xmlTextWriterWriteAttribute (x->content_wtr, _xml("xmlns:text"),
+ _xml("urn:oasis:names:tc:opendocument:xmlns:text:1.0"));
+
+ xmlTextWriterWriteAttribute (x->content_wtr, _xml("xmlns:table"),
+ _xml("urn:oasis:names:tc:opendocument:xmlns:table:1.0"));
+
+ xmlTextWriterWriteAttribute (x->content_wtr, _xml("office:version"), _xml("1.1"));
+
+ xmlTextWriterStartElement (x->content_wtr, _xml("office:body"));
+ xmlTextWriterStartElement (x->content_wtr, _xml("office:text"));
+
+
+
+ /* Close the manifest */
+ xmlTextWriterEndElement (x->manifest_wtr);
+ xmlTextWriterEndDocument (x->manifest_wtr);
+ xmlFreeTextWriter (x->manifest_wtr);
+
+ return true;
+}
+
+static bool
+odt_close_driver (struct outp_driver *this)
+{
+ struct string zip_cmd;
+ struct string rm_cmd;
+ struct odt_driver_ext *x = this->ext;
+
+ xmlTextWriterEndElement (x->content_wtr); /* office:text */
+ xmlTextWriterEndElement (x->content_wtr); /* office:body */
+ xmlTextWriterEndElement (x->content_wtr); /* office:document-content */
+
+ xmlTextWriterEndDocument (x->content_wtr);
+ xmlFreeTextWriter (x->content_wtr);
+
+ /* Zip up the directory */
+ ds_init_empty (&zip_cmd);
+ ds_put_format (&zip_cmd,
+ "cd %s ; rm -f ../%s; zip -q -X ../%s mimetype; zip -q -X -u -r ../pspp.odt .",
+ x->dirname, x->opts.file_name, x->opts.file_name);
+ system (ds_cstr (&zip_cmd));
+ ds_destroy (&zip_cmd);
+
+
+ if ( !x->opts.debug )
+ {
+ /* Remove the temp dir */
+ ds_init_empty (&rm_cmd);
+ ds_put_format (&rm_cmd, "rm -r %s", x->dirname);
+ system (ds_cstr (&rm_cmd));
+ ds_destroy (&rm_cmd);
+ }
+ else
+ fprintf (stderr, "Not removing directory %s\n", x->dirname);
+
+ free (x->dirname);
+ free (x);
+
+ return true;
+}
+
+static void
+odt_open_page (struct outp_driver *this UNUSED)
+{
+}
+
+static void
+odt_close_page (struct outp_driver *this UNUSED)
+{
+}
+
+static void
+odt_output_chart (struct outp_driver *this UNUSED, const struct chart *chart UNUSED)
+{
+ printf ("%s\n", __FUNCTION__);
+}
+
+
+/* Submit a table to the ODT driver */
+static void
+odt_submit (struct outp_driver *this, struct som_entity *e)
+{
+ int r, c;
+
+ struct odt_driver_ext *x = this->ext;
+ struct tab_table *tab = e->ext;
+
+
+ /* Write a heading for the table */
+ xmlTextWriterStartElement (x->content_wtr, _xml("text:h"));
+ xmlTextWriterWriteFormatAttribute (x->content_wtr, _xml("text:level"), "%d", e->subtable_num == 1 ? 2 : 3);
+ xmlTextWriterWriteString (x->content_wtr, _xml (tab->title) );
+ xmlTextWriterEndElement (x->content_wtr);
+
+ /* Start table */
+ xmlTextWriterStartElement (x->content_wtr, _xml("table:table"));
+ xmlTextWriterWriteFormatAttribute (x->content_wtr, _xml("table:name"),
+ "TABLE-%d.%d", e->table_num, e->subtable_num);
+
+
+ /* Start column definitions */
+ xmlTextWriterStartElement (x->content_wtr, _xml("table:table-column"));
+ xmlTextWriterWriteFormatAttribute (x->content_wtr, _xml("table:number-columns-repeated"), "%d", tab_nc (tab));
+ xmlTextWriterEndElement (x->content_wtr);
+
+
+ /* Deal with row headers */
+ if ( tab_t (tab) > 0)
+ xmlTextWriterStartElement (x->content_wtr, _xml("table:table-header-rows"));
+
+
+ /* Write all the rows */
+ for (r = 0 ; r < tab_nr (tab); ++r)
+ {
+ int spanned_columns = 0;
+ /* Start row definition */
+ xmlTextWriterStartElement (x->content_wtr, _xml("table:table-row"));
+
+ /* Write all the columns */
+ for (c = 0 ; c < tab_nc (tab) ; ++c)
+ {
+ char *s = NULL;
+ unsigned int opts = tab->ct[tab_nc (tab) * r + c];
+ struct substring ss = tab->cc[tab_nc (tab) * r + c];
+
+ if (opts & TAB_EMPTY)
+ {
+ xmlTextWriterStartElement (x->content_wtr, _xml("table:table-cell"));
+ xmlTextWriterEndElement (x->content_wtr);
+ continue;
+ }
+
+ if ( opts & TAB_JOIN)
+ {
+ if ( spanned_columns == 0)
+ {
+ struct tab_joined_cell *j = (struct tab_joined_cell*) ss_data (ss);
+ s = ss_xstrdup (j->contents);
+ }
+ }
+ else
+ s = ss_xstrdup (ss);
+
+ if ( spanned_columns == 0 )
+ {
+ xmlTextWriterStartElement (x->content_wtr, _xml("table:table-cell"));
+ xmlTextWriterWriteAttribute (x->content_wtr, _xml("office:value-type"), _xml("string"));
+
+ if ( opts & TAB_JOIN )
+ {
+ struct tab_joined_cell *j = (struct tab_joined_cell*) ss_data (ss);
+ spanned_columns = j->x2 - j->x1;
+
+ xmlTextWriterWriteFormatAttribute (x->content_wtr,
+ _xml("table:number-columns-spanned"),
+ "%d", spanned_columns);
+ }
+
+ xmlTextWriterStartElement (x->content_wtr, _xml("text:p"));
+
+ if ( r < tab_t (tab) || c < tab_l (tab) )
+ xmlTextWriterWriteAttribute (x->content_wtr, _xml("text:style-name"), _xml("Table_20_Heading"));
+ else
+ xmlTextWriterWriteAttribute (x->content_wtr, _xml("text:style-name"), _xml("Table_20_Contents"));
+
+ xmlTextWriterWriteString (x->content_wtr, _xml (s));
+
+ xmlTextWriterEndElement (x->content_wtr); /* text:p */
+ xmlTextWriterEndElement (x->content_wtr); /* table:table-cell */
+ }
+ else
+ {
+ xmlTextWriterStartElement (x->content_wtr, _xml("table:covered-table-cell"));
+ xmlTextWriterEndElement (x->content_wtr);
+ }
+ if ( opts & TAB_JOIN )
+ spanned_columns --;
+
+ free (s);
+ }
+
+ xmlTextWriterEndElement (x->content_wtr); /* row */
+
+ if ( tab_t (tab) > 0 && r == tab_t (tab) - 1)
+ xmlTextWriterEndElement (x->content_wtr); /* table-header-rows */
+ }
+
+ xmlTextWriterEndElement (x->content_wtr); /* table */
+}
+
+
+/* ODT driver class. */
+const struct outp_class odt_class =
+{
+ "odf",
+ 1,
+
+ odt_open_driver,
+ odt_close_driver,
+
+ odt_open_page,
+ odt_close_page,
+ NULL,
+
+ odt_output_chart,
+ odt_submit,
+
+ NULL,
+ NULL,
+ NULL,
+};
/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include "gettext.h"
#define _(msgid) gettext (msgid)
-/* FIXME? Should the output configuration format be changed to
- drivername:classname:devicetype:options, where devicetype is zero
- or more of screen, printer, listing? */
-
-/* FIXME: Have the reentrancy problems been solved? */
-
/* Where the output driver name came from. */
enum
{
};
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;
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);
}
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 ();
}
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)
{
}
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;
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));
{
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);
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;
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. */
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:
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
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;
}
/* 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
#ifndef OUTPUT_OUTPUT_H
#define OUTPUT_OUTPUT_H 1
+#include <libpspp/ll.h>
#include <libpspp/str.h>
-
/* Line styles. */
enum outp_line_style
{
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 *);
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 *);
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. */
/* 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. */
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. */
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 *);
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 *);
/* 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 */
/* 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
#include <time.h>
#include <unistd.h>
+#include <data/file-name.h>
#include <libpspp/assertion.h>
#include <libpspp/bit-vector.h>
#include <libpspp/compiler.h>
#include <libpspp/misc.h>
#include <libpspp/start-date.h>
#include <libpspp/version.h>
+#include <output/afm.h>
+#include <output/chart-provider.h>
+#include <output/chart.h>
+#include <output/manager.h>
+#include <output/output.h>
-#include <data/file-name.h>
-
-#include "afm.h"
-#include "chart.h"
#include "error.h"
-#include "manager.h"
-#include "output.h"
-
#include "intprops.h"
#include "minmax.h"
#include "xalloc.h"
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);
/* 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;
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)
write_ps_prologue (this);
+ outp_register_driver (this);
return true;
error:
this->class->close_driver (this);
+ outp_free_driver (this);
return false;
}
};
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);
{
switch (s->type)
{
- case SOM_CHART:
- break;
default:
NOT_REACHED ();
}
text (this, t, true, NULL, NULL);
}
\f
-static void
-ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch)
-{
-#ifdef NO_CHARTS
- ch->lp = NULL;
-#else
- struct ps_driver_ext *x = this->ext;
- char page_size[128];
- int size;
- int x_origin, y_origin;
-
- ch->file = tmpfile ();
- if (ch->file == NULL)
- {
- ch->lp = NULL;
- return;
- }
-
- size = this->width < this->length ? this->width : this->length;
- x_origin = x->left_margin + (size - this->width) / 2;
- y_origin = x->bottom_margin + (size - this->length) / 2;
-
- snprintf (page_size, sizeof page_size,
- "a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
- (double) size / PSUS, (double) size / PSUS,
- (double) x_origin / PSUS, (double) y_origin / PSUS);
-
- ch->pl_params = pl_newplparams ();
- pl_setplparam (ch->pl_params, "PAGESIZE", page_size);
- ch->lp = pl_newpl_r ("ps", NULL, ch->file, stderr, ch->pl_params);
-#endif
-}
-
-static void
-ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
-{
-#ifndef NO_CHARTS
- struct ps_driver_ext *x = this->ext;
- char buf[BUFSIZ];
- static int doc_num = 0;
-
- outp_eject_page (this);
- fprintf (x->file,
- "/sp save def\n"
- "%d %d translate 1000 dup scale\n"
- "userdict begin\n"
- "/showpage { } def\n"
- "0 setgray 0 setlinecap 1 setlinewidth\n"
- "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n"
- "%%%%BeginDocument: %d\n",
- -x->left_margin, -x->bottom_margin,
- doc_num++);
-
- rewind (ch->file);
- while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file))
- continue;
- fclose (ch->file);
-
- fputs ("%%EndDocument\n"
- "end\n"
- "sp restore\n",
- x->file);
- outp_close_page (this);
-#endif
-}
-\f
static void embed_font (struct outp_driver *this, struct font *font);
static void reencode_font (struct outp_driver *this, struct font *font);
ps_close_page,
NULL,
+ NULL, /* output_chart */
+
ps_submit,
ps_line,
ps_text_metrics,
ps_text_draw,
-
- ps_chart_initialise,
- ps_chart_finalise
};
#include <data/settings.h>
+#include "error.h"
#include "minmax.h"
#include "xalloc.h"
#define _(msgid) gettext (msgid)
\f
const struct som_table_class tab_table_class;
-static char *command_name;
/* Returns the font to use for a cell with the given OPTIONS. */
static enum outp_font
/* 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;
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. */
}
if (nr != -1)
{
- assert (nr + t->row_ofs <= t->nr);
+ assert (nr + t->row_ofs <= tab_nr (t));
t->nr = nr + t->row_ofs;
}
}
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;
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);
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);
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;
/* Set up table T so that, when it is an appropriate size, it will be
displayed across the page in columns.
- STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
- as a unit. */
+ STYLE is a TAB_COL_* constant. */
void
-tab_columns (struct tab_table *t, int style, int group)
+tab_columns (struct tab_table *t, int style)
{
assert (t != NULL);
t->col_style = style;
- t->col_group = group;
}
\f
/* Rules. */
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
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)
{
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)
{
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"),
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
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)
{
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;
}
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;
}
/* Callback function to set all columns and rows to their natural
dimensions. Not really meant to be called directly. */
void
-tab_natural_dimensions (struct tab_table *t, struct outp_driver *d,
- void *aux UNUSED)
+tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
{
+ const struct tab_table *t = r->table;
int i;
- assert (t != NULL);
-
- for (i = 0; i < t->nc; i++)
- t->w[i] = tab_natural_width (t, d, i);
+ for (i = 0; i < tab_nc (t); i++)
+ r->w[i] = tab_natural_width (r, i);
- for (i = 0; i < t->nr; i++)
- t->h[i] = tab_natural_height (t, d, i);
+ for (i = 0; i < tab_nr (t); i++)
+ r->h[i] = tab_natural_height (r, i);
}
\f
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
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
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 ();
#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
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
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",
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
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;
pool_vasprintf (table->container, format, args));
va_end (args);
}
-
-/* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
-void
-tab_raw (struct tab_table *table, int c, int r, unsigned opt,
- struct substring *string)
-{
- assert (table != NULL && string != NULL);
-
-#if DEBUGGING
- if (c + table->col_ofs < 0 || r + table->row_ofs < 0
- || c + table->col_ofs >= table->nc
- || r + table->row_ofs >= table->nr)
- {
- printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
- "(%d,%d)\n",
- c, table->col_ofs, c + table->col_ofs,
- r, table->row_ofs, r + table->row_ofs,
- table->nc, table->nr);
- return;
- }
-#endif
-
- table->cc[c + r * table->cf] = *string;
- table->ct[c + r * table->cf] = opt;
-}
\f
/* Miscellaneous. */
/* Sets the widths of all the columns and heights of all the rows in
table T for driver D. */
static void
-nowrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
{
- t->w[0] = tab_natural_width (t, d, 0);
- t->h[0] = d->font_height;
+ r->w[0] = tab_natural_width (r, 0);
+ r->h[0] = r->driver->font_height;
}
/* Sets the widths of all the columns and heights of all the rows in
table T for driver D. */
static void
-wrap_dim (struct tab_table *t, struct outp_driver *d, void *aux UNUSED)
+wrap_dim (struct tab_rendering *r, void *aux UNUSED)
{
- t->w[0] = tab_natural_width (t, d, 0);
- t->h[0] = tab_natural_height (t, d, 0);
+ r->w[0] = tab_natural_width (r, 0);
+ r->h[0] = tab_natural_height (r, 0);
}
static void
{
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);
}
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));
}
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,
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
assert (t != NULL);
t->cc += t->cf;
t->ct += t->cf;
- if (++t->row_ofs >= t->nr)
- tab_realloc (t, -1, t->nr * 4 / 3);
+ if (++t->row_ofs >= tab_nr (t))
+ tab_realloc (t, -1, tab_nr (t) * 4 / 3);
}
\f
-static struct tab_table *t;
-static struct outp_driver *d;
-static int tab_hit;
+/* Return the number of columns and rows in the table into N_COLUMNS
+ and N_ROWS, respectively. */
+static void
+tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
+{
+ struct tab_table *t = t_->ext;
+ *n_columns = t->nc;
+ *n_rows = t->nr;
+}
-/* Set the current table to TABLE. */
+/* Return the column style for this table into STYLE. */
static void
-tabi_table (struct som_entity *table)
+tabi_columns (struct som_entity *t_, int *style)
{
- assert (table != NULL);
- assert (table->type == SOM_TABLE);
+ struct tab_table *t = t_->ext;
+ *style = t->col_style;
+}
- t = table->ext;
- tab_offset (t, 0, 0);
+/* Return the number of header rows/columns on the left, right, top,
+ and bottom sides into HL, HR, HT, and HB, respectively. */
+static void
+tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
+{
+ struct tab_table *t = t_->ext;
+ *hl = t->l;
+ *hr = t->r;
+ *ht = t->t;
+ *hb = t->b;
+}
- assert (t->w == NULL && t->h == NULL);
- t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
- t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
- t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
- t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
+/* Return flags set for the current table into FLAGS. */
+static void
+tabi_flags (struct som_entity *t_, unsigned *flags)
+{
+ struct tab_table *t = t_->ext;
+ *flags = t->flags;
}
/* Returns the line style to use for spacing purposes for a rule
}
}
-/* 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
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)
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,
};
/* 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);
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;
}
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];
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
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;
-}
{
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. */
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);
/* 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);
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 *);
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 */
## 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
src/libpspp.la \
src/libpspp-core.la \
$(GTK_LIBS) \
+ $(CAIRO_LIBS) \
$(LIBINTL)
src_ui_gui_psppiredir = $(pkgdatadir)
som_flush ();
- psppire_output_window_reload ();
-
return retval;
}
#include <ctype.h>
#include <sys/types.h>
#include <regex.h>
+#include <libpspp/cast.h>
#include <libpspp/message.h>
#include <gtk/gtk.h>
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);
}
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));
}
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;
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;
{
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;
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.2.0 on Sat Aug 11 17:19:54 2007 by john@marilyn-->
+<?xml version="1.0"?>
<glade-interface>
+ <!-- interface-requires gtk+ 2.16 -->
+ <!-- interface-naming-policy toplevel-contextual -->
<widget class="GtkWindow" id="output-viewer-window">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="default_width">600</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkMenuItem" id="menuitem1">
- <property name="visible">False</property>
<property name="sensitive">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_File</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkImageMenuItem" id="file_save">
+ <property name="label">gtk-save</property>
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">gtk-save</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="file_save-as">
+ <property name="label">gtk-save-as</property>
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">gtk-save-as</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="menuitem2">
- <property name="visible">False</property>
<property name="sensitive">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Edit</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkImageMenuItem" id="imagemenuitem7">
+ <property name="label">gtk-copy</property>
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">gtk-copy</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</widget>
<packing>
<property name="expand">False</property>
+ <property name="position">0</property>
</packing>
</child>
<child>
- <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <widget class="GtkHPaned" id="hpaned1">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
- <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="position">112</property>
+ <property name="position_set">True</property>
<child>
- <widget class="GtkTextView" id="output-viewer-textview">
+ <widget class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <widget class="GtkTreeView" id="overview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="editable">False</property>
- <property name="left_margin">5</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <widget class="GtkLayout" id="output">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ </child>
</widget>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
</child>
</widget>
<packing>
/* PSPPIRE - a graphical user interface for PSPP.
- Copyright (C) 2008 Free Software Foundation
+ Copyright (C) 2008, 2009 Free Software Foundation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include "helper.h"
#include <libpspp/message.h>
+#include <output/cairo.h>
+#include <output/manager.h>
+#include <output/output.h>
+#include <output/table.h>
#include <stdlib.h>
#include "about.h"
#define _(msgid) gettext (msgid)
#define N_(msgid) msgid
-
+enum
+ {
+ COL_TITLE, /* Table title. */
+ COL_Y, /* Y position of top of title. */
+ N_COLS
+ };
static void psppire_output_window_base_finalize (PsppireOutputWindowClass *, gpointer);
static void psppire_output_window_base_init (PsppireOutputWindowClass *class);
gpointer class_data)
{
}
-
-
\f
+/* Output driver class. */
static PsppireOutputWindow *the_output_viewer = NULL;
+static gboolean
+expose_event_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
+{
+ struct som_entity *entity = g_object_get_data (G_OBJECT (widget), "entity");
+ GdkWindow *window = widget->window;
+ cairo_t *cairo = gdk_cairo_create (GDK_DRAWABLE (window));
+ struct outp_driver *driver = xr_create_driver (cairo); /* XXX can fail */
+ struct tab_table *t = entity->ext;
+ void *rendering;
+
+ rendering = entity->class->render_init (entity, driver, tab_l (t),
+ tab_r (t), tab_t (t), tab_b (t));
+
+ entity->class->title (rendering, 0, 0,
+ entity->table_num, entity->subtable_num,
+ entity->command_name);
+ entity->class->render (rendering, tab_l (t), tab_t (t),
+ tab_nc (t) - tab_r (t),
+ tab_nr (t) - tab_b (t));
+
+ entity->class->render_free (rendering);
+ driver->class->close_driver (driver);
+ outp_free_driver (driver);
+ return TRUE;
+}
+static void
+psppire_output_submit (struct outp_driver *this, struct som_entity *entity)
+{
+ if (the_output_viewer == NULL)
+ {
+ the_output_viewer = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
+ gtk_widget_show_all (GTK_WIDGET (the_output_viewer));
+ }
+
+ if (entity->type == SOM_TABLE)
+ {
+ GdkWindow *window = GTK_WIDGET (the_output_viewer)->window;
+ cairo_t *cairo = gdk_cairo_create (GDK_DRAWABLE (window));
+ struct outp_driver *driver = xr_create_driver (cairo); /* XXX can fail */
+ struct tab_table *t = entity->ext;
+ GtkTreeStore *store;
+ GtkTreeIter item;
+ GtkTreePath *path;
+ GtkWidget *drawing_area;
+ void *rendering;
+ struct string title;
+ int tw, th;
+
+ tab_ref (t);
+ rendering = entity->class->render_init (entity, driver, tab_l (t),
+ tab_r (t), tab_t (t), tab_b (t));
+ entity->class->area (rendering, &tw, &th);
+
+ drawing_area = gtk_drawing_area_new ();
+ gtk_widget_modify_bg (GTK_WIDGET (drawing_area), GTK_STATE_NORMAL,
+ >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));
+}
+\f
int viewer_length = 16;
int viewer_width = 59;
the_output_viewer = NULL;
- unlink (output_file_name());
-
return FALSE;
}
{
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),
"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);
-}
-
-
-
/* 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
extern int viewer_width ;
-#define OUTPUT_FILE_NAME "psppire.txt"
-
-
-
G_BEGIN_DECLS
#define PSPPIRE_OUTPUT_WINDOW_TYPE (psppire_output_window_get_type ())
PsppireWindow parent;
/* <private> */
- GtkTextBuffer *buffer; /* The buffer which contains the text */
- GtkWidget *textview ;
- FILE *fp; /* The file it's viewing */
+ GtkLayout *output;
+ int max_width;
+ int y;
+
+ GtkTreeView *overview;
+ int last_table_num;
+ GtkTreeIter last_top_level;
};
struct _PsppireOutputWindowClass
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
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);
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;
-}
/* 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
#include <libpspp/getl.h>
#include <libpspp/compiler.h>
+#include <libpspp/cast.h>
#include <libpspp/str.h>
#include <stdlib.h>
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;
ses->parent.location = location;
- return (struct getl_interface *) ses;
+ return &ses->parent;
}
struct val_labs_dialog *dialog = data;
union value value;
- const struct val_lab *vl;
+ struct val_lab *vl;
get_selected_tuple (dialog, &value, NULL);
vl = val_labs_lookup (dialog->labs, &value);
src/ui/libuicommon.la \
src/libpspp.la \
src/libpspp-core.la \
+ $(CAIRO_LIBS) \
$(NCURSES_LIBS) \
$(LIBICONV) \
$(LIBINTL) $(LIBREADLINE)
/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2007 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <data/file-name.h>
#include <data/settings.h>
#include <language/command.h>
+#include <libpspp/cast.h>
#include <libpspp/message.h>
#include <libpspp/str.h>
#include <libpspp/version.h>
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 ());
}
rlns->parent.read = read_interactive;
rlns->parent.close = readln_close;
- return (struct getl_interface *) rlns;
+ return &rlns->parent;
}
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);